本文へ移動
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.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

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/emanote9642026年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/emanote9642026年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/emanote9642026年7月26日 更新

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

日本語の概要は準備中です。原文の説明を表示しています。

srid/emanote9642026年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/emanote9642026年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/emanote9642026年7月26日 更新

srid のスキルをすべて見る

このスキルの問題を報告する