본문으로 건너뛰기
cccskills
무료GitHub에 공개

dpella-mcp

Reference for the dpella/mcp Haskell library — types, handler shape, transports, and the conventions Emanote uses. Load this before adding MCP resources/tools/subscriptions so you don't re-discover the API by WebFetch.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md7.8 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

dpella/mcp reference

Canonical source: https://github.com/dpella/mcp. Two Hackage packages: mcp-types (pure types, minimal deps) and mcp (Servant-based server with HTTP/Stdio transports). MCP protocol version 2025-06-18. Depends on jsonrpc (also a DPella package, not in nixpkgs).

Versions to pin

Use GitHub sources rather than Hackage when the target GHC's all-cabal-hashes snapshot lags behind:

# flake.nix inputs
dpella-mcp.url = "github:dpella/mcp";
dpella-mcp.flake = false;
dpella-jsonrpc.url = "github:dpella/jsonrpc";
dpella-jsonrpc.flake = false;
# haskell-flake
packages.mcp.source       = inputs.dpella-mcp + /mcp-server;
packages.mcp-types.source = inputs.dpella-mcp + /mcp-types;
packages.jsonrpc.source   = inputs.dpella-jsonrpc;

Supported GHC: 9.6–9.12 (via base >=4.18 && <4.22). servant-auth-server 0.4.x and warp 3.4.x are sibling deps.

Required type-family instances

Every user of MCP.Server must provide both:

type instance MCPHandlerState = YourSessionState
type instance MCPHandlerUser  = YourUserPayload  -- for JWT; use () when using simpleHttpApp

Instances must live in a module that gets compiled before initMCPServerState is called.

Three transports

TransportAuthEntry pointNotes
HTTP + JWTservant-auth-servermcpAPI stateVar via serveWithContext (Proxy @MCPAPI) ctxProduction web
Simple HTTPnonesimpleHttpApp stateVar :: ApplicationLocal/loopback only; pair with Warp.run or Warp.runSettings
StdiononeserveStdio stdin stdout initialStateSubprocess integrations

For HTTP use Warp.setBeforeMainLoop to log after the socket is bound — do not print before Warp.run/runSettings, the bind hasn't happened yet and the log lies.

initMCPServerState signature

initMCPServerState
  :: MCPHandlerState                                                  -- initial state
  -> Maybe (MCPHandlerUser -> MCPHandlerState -> IO MCPHandlerState)  -- onInitialize (JWT only)
  -> Maybe (MCPHandlerState -> IO MCPHandlerState)                    -- onFinalize (after each req)
  -> ServerCapabilities                                               -- what to advertise
  -> Implementation                                                   -- name/version/title
  -> Maybe Text                                                       -- free-text instructions
  -> ProcessHandlers                                                  -- the actual handlers
  -> MCPServerState

Wrap the result in an MVar — simpleHttpApp / mcpAPI both take MVar MCPServerState.

ServerCapabilities record

ServerCapabilities
  { logging      :: Maybe LoggingCapability
  , prompts      :: Maybe PromptsCapability    { listChanged :: Maybe Bool }
  , resources    :: Maybe ResourcesCapability  { listChanged, subscribe :: Maybe Bool }
  , tools        :: Maybe ToolsCapability      { listChanged :: Maybe Bool }
  , completions  :: Maybe CompletionsCapability
  , experimental :: Maybe ...
  }

Advertising Just … without providing a matching handler means clients will call it and hit the library's method_not_found. For Phase-1-style stubs, provide an empty-list handler (e.g. listResourcesHandler = Just (\_ -> pure $ ProcessSuccess ListResourcesResult{..})).

ProcessHandlers record

Every field is a Maybe — set only what the server implements:

  • listResourcesHandler, readResourceHandler, listResourceTemplatesHandler
  • listToolsHandler, callToolHandler — don't set manually; use withToolHandlers :: [ToolHandler] -> ProcessHandlers -> ProcessHandlers which wires both based on a list of ToolHandlers.
  • listPromptsHandler, getPromptHandler
  • completeHandler
  • subscribeHandler, unsubscribeHandler

Start from defaultProcessHandlers (all Nothing) and override.

ToolHandler construction

toolHandler
  :: Text                         -- name
  -> Maybe Text                   -- description
  -> InputSchema                  -- JSON schema for args
  -> (Maybe (Map Text Value)      -- handler body
        -> MCPServerT (ProcessResult CallToolResult))
  -> ToolHandler

InputSchema "object" (Just propsMap) (Just ["req1", "req2"]) is the typical shape. withToolHandlers validates required args before calling the handler.

Returning results: toolTextResult [Text] for plain text; build CallToolResult directly when you need structured output (structuredContent :: Maybe (Map Text Value)).

Logging

mcp_log_level :: Maybe LoggingLevel on MCPServerState — defaults to Just Warning. Set to Just Debug to get one [request] / [response] stdout line per JSON-RPC call (see MCP/Server/HTTP/Internal.hs). The library does not emit anything else on its own; wire your own startup log via Warp's setBeforeMainLoop.

Clients can also change the level at runtime via logging/setLevel if the server advertises logging = Just LoggingCapability.

ProcessResult shape

data ProcessResult a
  = ProcessSuccess a
  | ProcessRPCError Int Text             -- JSON-RPC error (400-ish, 404-ish, …)
  | ProcessServerError Text              -- internal
  | ProcessClientInput Text Value cont   -- request additional input from client (sampling, elicitation)

Return ProcessRPCError 404 ("resource not found: " <> uri) for unknown URIs; the library turns this into a well-formed JSON-RPC error.

DuplicateRecordFields is required

MCP types reuse name, _meta, title, etc. across many records (Implementation, Tool, Prompt, Resource, …). Enable {-# LANGUAGE DuplicateRecordFields #-} at the use site. Field disambiguation sometimes needs a qualified prefix: MCP.name = …, MCP._meta = … where import MCP.Server qualified as MCP.

Emanote-specific conventions (phase 1)

  • MCP runs beside the live server via UnliftIO.Async.race_ under the run subcommand only.
  • CLI flag: emanote run --mcp-port PORT (top-level --verbose flips library log level to Debug).
  • Startup line: [mcp] listening on http://localhost:PORT/mcp via Warp.setBeforeMainLoop.
  • Transport: simpleHttpApp (no auth). Authentication deferred per the issue's open question.
  • State types: MCPHandlerState = (), MCPHandlerUser = ().
  • Handlers advertise resources and tools capabilities; empty inventories today.
  • See emanote/src/Emanote/MCP.hs for the canonical shape.

Rollout tracker: srid/emanote#645. Phases 2–5 will add notebook-backed resources, query tools, subscriptions, and optional prompts respectively.

Common pitfalls

  1. Version tax: Hackage's mcp moves faster than nixpkgs' all-cabal-hashes. Pin GitHub sources in flake.nix inputs when the Hackage-via-nixpkgs path refuses to build.
  2. newMVar ambiguity: importing Control.Concurrent.MVar alongside Relude produces an ambiguous newMVar. Drop the explicit import — Relude's is fine.
  3. NamedFieldPuns with qualified imports: ReadResourceParams {uri} only works if uri is unqualified. If you import MCP.Server qualified, either enable NamedFieldPuns and use the unqualified name, or bind fields positionally.
  4. Ambiguous _meta: field is shared across a dozen records; with DuplicateRecordFields on, GHC often still demands a qualified prefix (MCP._meta) at construction sites. fourmolu will re-add the qualifier if removed.
  5. Don't advertise subscribe = Just True until the server actually implements subscribeHandler / unsubscribeHandler. The library has no built-in defaults for those.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

be

무료

Modern, interactive alternative to `/do` — clarify intent up front, then take a task end-to-end with a serial AI review gauntlet (lens debate (lowy ⇄ hickey) → codex debate → simplify → code-police, each editing the branch in turn) → CI → evidence. ONLY invoke when the user explicitly types `/be` or `$be`; never auto-select from a natural-language request.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

srid/emanote9662026년 7월 26일 업데이트

be-review

무료

Run /be's review gauntlet SERIALLY — /lens-debate (lowy ⇄ hickey), then /codex-debate, then /simplify, then code-police, each editing and committing on the live branch in turn. Use from /be §4, or when the user asks to "run the review gauntlet". Requires Claude Code's Skill tool.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

srid/emanote9662026년 7월 26일 업데이트

ci

무료

Reference for the `odu` runner — how to invoke a full pipeline, a single recipe, or a platform-pinned node, and how to attach to a live run, from a project whose CI odu runs. Trigger when the user asks to "run CI", "run the pipeline", "re-run a check", to run named lanes or recipes (e.g. "run fmt and nix", "just the e2e lane", bare selectors like `fmt`/`nix`/`e2e`), or names a recipe by `<recipe>@<platform>`. This skill — not a repo's local `just ci` / `just <recipe>` — is how an odu-run request is served.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

srid/emanote9662026년 7월 26일 업데이트

Review code for quality, simplicity, and common mistakes before declaring work complete.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

srid/emanote9662026년 7월 26일 업데이트

Run an automated codex⇄Claude debate to consensus — no round cap, no deadlock exit. Two explicit subcommands. `review` (also the bare/back-compat default) — codex (reviewer) critiques the current diff and a Claude subagent (author) fixes/disputes, looping until they agree. `answer` — Claude and codex each answer a freeform prompt in parallel, then cross-check until they agree, and a unified answer is returned. Use when the user types `/codex-debate`, asks to "have codex review this", "run the codex debate", "review this PR with codex", "argue this with codex until you agree", or passes a question to "have Claude and codex debate/answer until they agree".

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

srid/emanote9662026년 7월 26일 업데이트

do

무료

Do a task end-to-end — implement, PR, CI loop, ship. ONLY invoke when the user explicitly types `/do` or `$do`; never auto-select from a natural-language request, even one that sounds like an end-to-end task.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

srid/emanote9662026년 7월 26일 업데이트

srid의 스킬 모두 보기

이 스킬의 문제 신고