@directive-run/mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sizls ∞ Jason Comes
4
+
5
+ This project is dual-licensed under MIT OR Apache-2.0. You may choose
6
+ either license. See LICENSE-APACHE for the Apache License, Version 2.0.
7
+
8
+ ---
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # `@directive-run/mcp`
2
+
3
+ A Model Context Protocol server that exposes Directive to AI clients. Today's tools cover knowledge files, code examples, and Claude Code skill bundles — queryable at retrieval time instead of bundled as a static snapshot. The package is the umbrella for "Directive as an MCP server," with room to grow into runtime introspection, system state, and tooling.
4
+
5
+ Two transports ship in the same binary:
6
+
7
+ - **stdio** — the canonical MCP local-client pattern. Plugs into Claude Desktop, Cursor MCP, the MCP Inspector, and any other client that spawns an MCP server as a subprocess.
8
+ - **SSE (HTTP)** — for hosting at `mcp.directive.run` or a private MCP gateway. Production AI agents query the hosted endpoint at runtime; no embedding pipeline, no bundle bloat.
9
+
10
+ > **Client vs. server, two different packages.** This package is the MCP **server** side — it serves Directive to outside clients. If you want the **client** side — Directive AI agents calling out to external MCP servers (filesystem, GitHub, etc.) — that's `@directive-run/ai/mcp` (`createMCPAdapter`). Same protocol, opposite arrows.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install -g @directive-run/mcp
16
+ # or run directly without installing:
17
+ npx @directive-run/mcp --help
18
+ ```
19
+
20
+ ## stdio transport (local clients)
21
+
22
+ Add to your Claude Desktop config (`claude_desktop_config.json`):
23
+
24
+ ```json
25
+ {
26
+ "mcpServers": {
27
+ "directive": {
28
+ "command": "npx",
29
+ "args": ["-y", "@directive-run/mcp"]
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ Or test with the MCP Inspector:
36
+
37
+ ```bash
38
+ npx @modelcontextprotocol/inspector npx -y @directive-run/mcp
39
+ ```
40
+
41
+ ## SSE transport (hosted)
42
+
43
+ ```bash
44
+ directive-mcp --sse --port 3000 --host 0.0.0.0
45
+ ```
46
+
47
+ Endpoints:
48
+
49
+ - `GET /sse` — establish the SSE stream.
50
+ - `POST /messages?sessionId=…` — client→server JSON-RPC messages.
51
+ - `GET /healthz` — liveness probe.
52
+
53
+ ## Tools
54
+
55
+ | Tool | Purpose |
56
+ |---|---|
57
+ | `list_knowledge` | Every knowledge file name (core + AI + skeleton). |
58
+ | `get_knowledge` | Read one knowledge file by name. |
59
+ | `list_examples` | Every code example name. |
60
+ | `get_example` | Read one example by name (returned as a TypeScript code block). |
61
+ | `search_knowledge` | Case-insensitive substring search across every knowledge file. |
62
+ | `list_skills` | Every Claude Code skill bundled in `@directive-run/claude-plugin`. |
63
+ | `get_skill` | One skill's `SKILL.md` + supporting knowledge files as a single document. |
64
+
65
+ ## Programmatic embedding
66
+
67
+ For tool authors who want to mount the server inside their own host process:
68
+
69
+ ```typescript
70
+ import { createDirectiveServer, startSseServer } from "@directive-run/mcp";
71
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
72
+
73
+ // stdio
74
+ const server = createDirectiveServer();
75
+ await server.connect(new StdioServerTransport());
76
+
77
+ // SSE — returns the underlying http.Server
78
+ const httpServer = await startSseServer({ port: 3000, host: "0.0.0.0" });
79
+ ```
80
+
81
+ ## See also
82
+
83
+ - [`@directive-run/ai/mcp`](../ai) — the MCP **client** side of Directive. Use it inside a Directive AI agent to call out to external MCP servers.
84
+ - [`@directive-run/knowledge`](../knowledge) — the knowledge package this server fronts.
85
+ - [`@directive-run/claude-plugin`](../claude-plugin) — the Claude Code skill bundles also exposed.
86
+ - [`@directive-run/cli`](../cli) — generate static `.cursorrules` / `CLAUDE.md` / `.windsurfrules` files for assistants that don't speak MCP.
87
+ - [docs/ide-integration](https://directive.run/docs/ide-integration) — decision tree across every install path.
package/dist/cli.js ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+ import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {getAllSkills,getSkill}from'@directive-run/claude-plugin';import {getAllKnowledge,getKnowledge,getAllExamples,getExample}from'@directive-run/knowledge';import {McpServer}from'@modelcontextprotocol/sdk/server/mcp.js';import {z}from'zod';import {createServer}from'http';import {SSEServerTransport}from'@modelcontextprotocol/sdk/server/sse.js';var $="0.1.0",g=50,m=200;function b(n){return n.length>m?`${n.slice(0,m)}\u2026`:n}function T(n,e){let t=n.toLowerCase(),s=[];for(let[r,o]of e){let a=o.split(`
3
+ `);for(let i=0;i<a.length;i++){let c=a[i];if(c.toLowerCase().includes(t)&&(s.push(`${r}.md:${i+1}: ${b(c)}`),s.length>=g))return s}}return s}function p(){let n=new McpServer({name:"directive",version:$});return n.registerTool("list_knowledge",{title:"List Directive knowledge files",description:"List every knowledge file shipped in @directive-run/knowledge. Returns the file names (without .md) that can be passed to get_knowledge. Covers core docs (engine, facts, constraints, resolvers, derivations, effects, plugins, modules, systems, testing) and AI docs (orchestrator, agents, adapters, guardrails, memory, MCP, RAG, security, evals, budget, multi-agent).",inputSchema:{}},async()=>{let e=getAllKnowledge(),t=Array.from(e.keys()).sort();return {content:[{type:"text",text:`${t.length} knowledge files:
4
+ ${t.join(`
5
+ `)}`}]}}),n.registerTool("get_knowledge",{title:"Get a Directive knowledge file",description:"Fetch the full Markdown contents of one Directive knowledge file by name. Use list_knowledge first to discover available names. Names match the file stem (e.g. 'constraints', 'ai-orchestrator', 'api-skeleton').",inputSchema:{name:z.string().min(1).describe("The knowledge file name (no .md suffix). Example: 'constraints', 'ai-orchestrator', 'api-skeleton'.")}},async({name:e})=>{let t=getKnowledge(e);return t?{content:[{type:"text",text:t}]}:{isError:true,content:[{type:"text",text:`Knowledge file not found: '${e}'. Call list_knowledge to see available names.`}]}}),n.registerTool("list_examples",{title:"List Directive code examples",description:"List every code example shipped in @directive-run/knowledge. Examples are minimal, working TypeScript files demonstrating one concept each. Pass the returned names to get_example.",inputSchema:{}},async()=>{let e=getAllExamples(),t=Array.from(e.keys()).sort();return {content:[{type:"text",text:`${t.length} examples:
6
+ ${t.join(`
7
+ `)}`}]}}),n.registerTool("get_example",{title:"Get a Directive code example",description:"Fetch the source of one Directive code example by name. Use list_examples first to discover available names. Returns raw TypeScript.",inputSchema:{name:z.string().min(1).describe("The example file name (no .ts suffix). Example: 'basic-module', 'ai-orchestrator'.")}},async({name:e})=>{let t=getExample(e);return t?{content:[{type:"text",text:`\`\`\`typescript
8
+ ${t}
9
+ \`\`\``}]}:{isError:true,content:[{type:"text",text:`Example not found: '${e}'. Call list_examples to see available names.`}]}}),n.registerTool("search_knowledge",{title:"Search Directive knowledge files",description:"Case-insensitive substring search across every knowledge file. Returns up to 50 matching lines with the file name and line context. Useful for discovering which knowledge file covers a topic before calling get_knowledge for the full document.",inputSchema:{query:z.string().min(1).describe("The search string. Matched case-insensitively against every line of every knowledge file.")}},async({query:e})=>{let t=T(e,getAllKnowledge());return t.length===0?{content:[{type:"text",text:`No matches for '${e}'.`}]}:{content:[{type:"text",text:`${t.length===g?`${t.length}+ matches (truncated):`:`${t.length} matches:`}
10
+ ${t.join(`
11
+ `)}`}]}}),n.registerTool("list_skills",{title:"List Directive Claude Code skills",description:"List every skill bundled in @directive-run/claude-plugin. Each skill is a gerund-named bundle of one SKILL.md plus supporting knowledge files. Pass a returned name to get_skill.",inputSchema:{}},async()=>{let e=getAllSkills(),t=Array.from(e.keys()).sort();return {content:[{type:"text",text:`${t.length} skills:
12
+ ${t.join(`
13
+ `)}`}]}}),n.registerTool("get_skill",{title:"Get a Directive Claude Code skill",description:"Fetch one skill bundle: the SKILL.md manifest plus every supporting knowledge file concatenated into a single document. Use list_skills to discover names.",inputSchema:{name:z.string().min(1).describe("The skill name (e.g. 'building-ai-orchestrators', 'writing-directive-constraints').")}},async({name:e})=>{let t=getSkill(e);if(!t)return {isError:true,content:[{type:"text",text:`Skill not found: '${e}'. Call list_skills to see available names.`}]};let s=[`# Skill: ${t.name}
14
+
15
+ ${t.manifest}`];for(let[r,o]of t.files)s.push(`---
16
+
17
+ ## ${r}.md
18
+
19
+ ${o}`);return {content:[{type:"text",text:s.join(`
20
+
21
+ `)}]}}),n}var u="/messages",h="/sse",_="/healthz";async function C(n,e,t){let s=new SSEServerTransport(u,n),r=p();e.set(s.sessionId,{transport:s});let o=()=>{e.delete(s.sessionId);};n.on("close",o),s.onclose=o,await r.connect(s),t.log(`[directive-mcp] sse session opened: ${s.sessionId}`);}async function I(n,e,t,s){let r=t.searchParams.get("sessionId");if(!r){e.writeHead(400,{"Content-Type":"text/plain"}),e.end("missing sessionId query parameter");return}let o=s.get(r);if(!o){e.writeHead(404,{"Content-Type":"text/plain"}),e.end(`unknown session: ${r}`);return}await o.transport.handlePostMessage(n,e);}async function L(n,e,t,s,r){let o=new URL(n.url??"/",`http://${n.headers.host??r}`);if(n.method==="GET"&&o.pathname===_){e.writeHead(200,{"Content-Type":"text/plain"}),e.end("ok");return}if(n.method==="GET"&&o.pathname===h){await C(e,t,s);return}if(n.method==="POST"&&o.pathname===u){await I(n,e,o,t);return}e.writeHead(404,{"Content-Type":"text/plain"}),e.end("not found");}async function f(n={}){let e=n.port??3e3,t=n.host??"127.0.0.1",s=n.logger??console,r=new Map,o=createServer(async(a,i)=>{try{await L(a,i,r,s,t);}catch(c){s.error("[directive-mcp] request error:",c),i.headersSent||i.writeHead(500,{"Content-Type":"text/plain"}),i.end("internal server error");}});return await new Promise(a=>{o.listen(e,t,()=>{s.log(`[directive-mcp] sse server listening at http://${t}:${e}${h}`),a();});}),o}var M="0.1.0",v=`directive-mcp \u2014 MCP server exposing Directive to AI clients
22
+
23
+ Usage:
24
+ directive-mcp Run stdio transport (default)
25
+ directive-mcp --sse Run SSE transport on 127.0.0.1:3000
26
+ directive-mcp --sse --port 8080 --host 0.0.0.0
27
+
28
+ Options:
29
+ --sse Use HTTP SSE transport instead of stdio
30
+ --port <port> SSE port (default: 3000)
31
+ --host <host> SSE bind host (default: 127.0.0.1)
32
+ --help, -h Show this help
33
+ --version, -v Show package version
34
+
35
+ Docs: https://directive.run/docs/ide-integration
36
+ `;function D(n){let e={sse:false,port:3e3,host:"127.0.0.1",help:false,version:false};for(let t=0;t<n.length;t++){let s=n[t];switch(s){case "--sse":e.sse=true;break;case "--port":{let r=n[++t];if(!r)throw new Error("--port requires a value");let o=Number(r);if(!Number.isInteger(o)||o<=0||o>65535)throw new Error(`invalid --port: ${r}`);e.port=o;break}case "--host":{let r=n[++t];if(!r)throw new Error("--host requires a value");e.host=r;break}case "--help":case "-h":e.help=true;break;case "--version":case "-v":e.version=true;break;default:throw new Error(`unknown argument: ${s}`)}}return e}async function H(){let n;try{n=D(process.argv.slice(2));}catch(s){process.stderr.write(`${s.message}
37
+
38
+ ${v}`),process.exit(2);}if(n.help){process.stdout.write(v);return}if(n.version){process.stdout.write(`${M}
39
+ `);return}if(n.sse){let s=await f({port:n.port,host:n.host}),r=()=>{s.close(()=>process.exit(0));};process.on("SIGINT",r),process.on("SIGTERM",r);return}let e=p(),t=new StdioServerTransport;await e.connect(t);}H().catch(n=>{process.stderr.write(`[directive-mcp] fatal: ${n.stack??String(n)}
40
+ `),process.exit(1);});//# sourceMappingURL=cli.js.map
41
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts","../src/sse.ts","../src/cli.ts"],"names":["PKG_VERSION","MAX_SEARCH_RESULTS","MAX_LINE_PREVIEW","formatLinePreview","line","collectSearchHits","query","knowledge","lowered","hits","name","content","lines","createDirectiveServer","server","McpServer","getAllKnowledge","names","z","getKnowledge","examples","getAllExamples","getExample","skills","getAllSkills","skill","getSkill","parts","fileName","MESSAGES_PATH","SSE_PATH","HEALTHZ_PATH","handleSseConnect","res","sessions","logger","transport","SSEServerTransport","cleanup","handlePostMessage","req","url","sessionId","session","routeRequest","host","startSseServer","options","port","httpServer","createServer","err","resolve","VERSION","USAGE","parseArgs","argv","result","i","arg","next","n","main","args","shutdown","StdioServerTransport"],"mappings":"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","file":"cli.js","sourcesContent":["/**\n * Build a Model Context Protocol server that exposes Directive to MCP\n * clients. Transport-agnostic — call `createDirectiveServer()` to get a\n * configured `McpServer`, then connect it to a stdio transport (local\n * clients) or an SSE transport (hosted at `mcp.directive.run`).\n *\n * This is the SERVER side of the protocol. For the CLIENT side —\n * Directive AI agents calling out to external MCP servers — see\n * `@directive-run/ai/mcp` (`createMCPAdapter`).\n *\n * Current tool surface (knowledge + skill bundles):\n *\n * - `list_knowledge` — every knowledge file name (core + AI + skeleton).\n * - `get_knowledge` — read one knowledge file by name.\n * - `list_examples` — every example file name.\n * - `get_example` — read one example by name.\n * - `search_knowledge` — case-insensitive substring search across all\n * knowledge files, returning the file name + matching lines.\n * - `list_skills` — every Claude Code skill bundled in the plugin.\n * - `get_skill` — read one skill's `SKILL.md` plus its supporting\n * files as a single concatenated document.\n *\n * The package is the umbrella for \"Directive as an MCP server\" — future\n * additions can expose runtime introspection, system state, CLI\n * commands, and debug snapshots through the same binary.\n */\n\nimport { getAllSkills, getSkill } from \"@directive-run/claude-plugin\";\nimport {\n getAllExamples,\n getAllKnowledge,\n getExample,\n getKnowledge,\n} from \"@directive-run/knowledge\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\n\nconst PKG_VERSION = \"0.1.0\";\n\nconst MAX_SEARCH_RESULTS = 50;\nconst MAX_LINE_PREVIEW = 200;\n\nfunction formatLinePreview(line: string): string {\n return line.length > MAX_LINE_PREVIEW\n ? `${line.slice(0, MAX_LINE_PREVIEW)}…`\n : line;\n}\n\nfunction collectSearchHits(\n query: string,\n knowledge: ReadonlyMap<string, string>,\n): string[] {\n const lowered = query.toLowerCase();\n const hits: string[] = [];\n\n for (const [name, content] of knowledge) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n if (!line.toLowerCase().includes(lowered)) {\n continue;\n }\n hits.push(`${name}.md:${i + 1}: ${formatLinePreview(line)}`);\n if (hits.length >= MAX_SEARCH_RESULTS) {\n return hits;\n }\n }\n }\n\n return hits;\n}\n\n/**\n * Build the MCP server with every Directive tool registered.\n *\n * The returned server has no transport attached — connect it to\n * `StdioServerTransport` for local clients or an SSE transport for\n * hosted deployments.\n */\nexport function createDirectiveServer(): McpServer {\n const server = new McpServer({\n name: \"directive\",\n version: PKG_VERSION,\n });\n\n server.registerTool(\n \"list_knowledge\",\n {\n title: \"List Directive knowledge files\",\n description:\n \"List every knowledge file shipped in @directive-run/knowledge. Returns the file names (without .md) that can be passed to get_knowledge. Covers core docs (engine, facts, constraints, resolvers, derivations, effects, plugins, modules, systems, testing) and AI docs (orchestrator, agents, adapters, guardrails, memory, MCP, RAG, security, evals, budget, multi-agent).\",\n inputSchema: {},\n },\n async () => {\n const knowledge = getAllKnowledge();\n const names = Array.from(knowledge.keys()).sort();\n\n return {\n content: [\n {\n type: \"text\",\n text: `${names.length} knowledge files:\\n${names.join(\"\\n\")}`,\n },\n ],\n };\n },\n );\n\n server.registerTool(\n \"get_knowledge\",\n {\n title: \"Get a Directive knowledge file\",\n description:\n \"Fetch the full Markdown contents of one Directive knowledge file by name. Use list_knowledge first to discover available names. Names match the file stem (e.g. 'constraints', 'ai-orchestrator', 'api-skeleton').\",\n inputSchema: {\n name: z\n .string()\n .min(1)\n .describe(\n \"The knowledge file name (no .md suffix). Example: 'constraints', 'ai-orchestrator', 'api-skeleton'.\",\n ),\n },\n },\n async ({ name }) => {\n const content = getKnowledge(name);\n if (!content) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `Knowledge file not found: '${name}'. Call list_knowledge to see available names.`,\n },\n ],\n };\n }\n\n return {\n content: [{ type: \"text\", text: content }],\n };\n },\n );\n\n server.registerTool(\n \"list_examples\",\n {\n title: \"List Directive code examples\",\n description:\n \"List every code example shipped in @directive-run/knowledge. Examples are minimal, working TypeScript files demonstrating one concept each. Pass the returned names to get_example.\",\n inputSchema: {},\n },\n async () => {\n const examples = getAllExamples();\n const names = Array.from(examples.keys()).sort();\n\n return {\n content: [\n {\n type: \"text\",\n text: `${names.length} examples:\\n${names.join(\"\\n\")}`,\n },\n ],\n };\n },\n );\n\n server.registerTool(\n \"get_example\",\n {\n title: \"Get a Directive code example\",\n description:\n \"Fetch the source of one Directive code example by name. Use list_examples first to discover available names. Returns raw TypeScript.\",\n inputSchema: {\n name: z\n .string()\n .min(1)\n .describe(\n \"The example file name (no .ts suffix). Example: 'basic-module', 'ai-orchestrator'.\",\n ),\n },\n },\n async ({ name }) => {\n const content = getExample(name);\n if (!content) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `Example not found: '${name}'. Call list_examples to see available names.`,\n },\n ],\n };\n }\n\n return {\n content: [\n { type: \"text\", text: `\\`\\`\\`typescript\\n${content}\\n\\`\\`\\`` },\n ],\n };\n },\n );\n\n server.registerTool(\n \"search_knowledge\",\n {\n title: \"Search Directive knowledge files\",\n description:\n \"Case-insensitive substring search across every knowledge file. Returns up to 50 matching lines with the file name and line context. Useful for discovering which knowledge file covers a topic before calling get_knowledge for the full document.\",\n inputSchema: {\n query: z\n .string()\n .min(1)\n .describe(\n \"The search string. Matched case-insensitively against every line of every knowledge file.\",\n ),\n },\n },\n async ({ query }) => {\n const hits = collectSearchHits(query, getAllKnowledge());\n\n if (hits.length === 0) {\n return {\n content: [{ type: \"text\", text: `No matches for '${query}'.` }],\n };\n }\n\n const header =\n hits.length === MAX_SEARCH_RESULTS\n ? `${hits.length}+ matches (truncated):`\n : `${hits.length} matches:`;\n\n return {\n content: [{ type: \"text\", text: `${header}\\n${hits.join(\"\\n\")}` }],\n };\n },\n );\n\n server.registerTool(\n \"list_skills\",\n {\n title: \"List Directive Claude Code skills\",\n description:\n \"List every skill bundled in @directive-run/claude-plugin. Each skill is a gerund-named bundle of one SKILL.md plus supporting knowledge files. Pass a returned name to get_skill.\",\n inputSchema: {},\n },\n async () => {\n const skills = getAllSkills();\n const names = Array.from(skills.keys()).sort();\n\n return {\n content: [\n {\n type: \"text\",\n text: `${names.length} skills:\\n${names.join(\"\\n\")}`,\n },\n ],\n };\n },\n );\n\n server.registerTool(\n \"get_skill\",\n {\n title: \"Get a Directive Claude Code skill\",\n description:\n \"Fetch one skill bundle: the SKILL.md manifest plus every supporting knowledge file concatenated into a single document. Use list_skills to discover names.\",\n inputSchema: {\n name: z\n .string()\n .min(1)\n .describe(\n \"The skill name (e.g. 'building-ai-orchestrators', 'writing-directive-constraints').\",\n ),\n },\n },\n async ({ name }) => {\n const skill = getSkill(name);\n if (!skill) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `Skill not found: '${name}'. Call list_skills to see available names.`,\n },\n ],\n };\n }\n\n const parts = [`# Skill: ${skill.name}\\n\\n${skill.manifest}`];\n for (const [fileName, content] of skill.files) {\n parts.push(`---\\n\\n## ${fileName}.md\\n\\n${content}`);\n }\n\n return {\n content: [{ type: \"text\", text: parts.join(\"\\n\\n\") }],\n };\n },\n );\n\n return server;\n}\n","/**\n * SSE (Server-Sent Events) transport for the Directive knowledge MCP\n * server. Hosts the server over HTTP so remote MCP clients\n * (production AI agents, the docs site, third-party tools) can query\n * it at retrieval time — borrowed pattern from zustand's\n * `docs.pmnd.rs/api/sse`.\n *\n * One SSE connection per client. Each connection gets its own\n * MCP server instance so per-session state (subscriptions, list\n * cursors) stays isolated.\n *\n * Endpoints:\n *\n * - `GET /sse` — establish the SSE stream. Server sends a session\n * ID and the client connects `/messages?sessionId=…` for the\n * POST half of the channel.\n * - `POST /messages?sessionId=…` — client→server JSON-RPC messages.\n * - `GET /healthz` — liveness probe returning 200 + `ok`.\n */\n\nimport { type IncomingMessage, type Server, createServer } from \"node:http\";\nimport { SSEServerTransport } from \"@modelcontextprotocol/sdk/server/sse.js\";\nimport { createDirectiveServer } from \"./server.js\";\n\nconst MESSAGES_PATH = \"/messages\";\nconst SSE_PATH = \"/sse\";\nconst HEALTHZ_PATH = \"/healthz\";\n\nexport interface SseServerOptions {\n /** Port to listen on. Default: 3000. */\n port?: number;\n /** Host to bind to. Default: `127.0.0.1`. Use `0.0.0.0` for public deployments. */\n host?: string;\n /** Optional logger. Default: `console`. */\n logger?: Pick<Console, \"log\" | \"warn\" | \"error\">;\n}\n\ninterface ActiveSession {\n transport: SSEServerTransport;\n}\n\ntype Sessions = Map<string, ActiveSession>;\ntype Logger = Pick<Console, \"log\" | \"warn\" | \"error\">;\n\nasync function handleSseConnect(\n res: import(\"node:http\").ServerResponse,\n sessions: Sessions,\n logger: Logger,\n): Promise<void> {\n const transport = new SSEServerTransport(MESSAGES_PATH, res);\n const server = createDirectiveServer();\n sessions.set(transport.sessionId, { transport });\n\n const cleanup = () => {\n sessions.delete(transport.sessionId);\n };\n res.on(\"close\", cleanup);\n transport.onclose = cleanup;\n\n await server.connect(transport);\n logger.log(`[directive-mcp] sse session opened: ${transport.sessionId}`);\n}\n\nasync function handlePostMessage(\n req: IncomingMessage,\n res: import(\"node:http\").ServerResponse,\n url: URL,\n sessions: Sessions,\n): Promise<void> {\n const sessionId = url.searchParams.get(\"sessionId\");\n if (!sessionId) {\n res.writeHead(400, { \"Content-Type\": \"text/plain\" });\n res.end(\"missing sessionId query parameter\");\n return;\n }\n\n const session = sessions.get(sessionId);\n if (!session) {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(`unknown session: ${sessionId}`);\n return;\n }\n\n await session.transport.handlePostMessage(req, res);\n}\n\nasync function routeRequest(\n req: IncomingMessage,\n res: import(\"node:http\").ServerResponse,\n sessions: Sessions,\n logger: Logger,\n host: string,\n): Promise<void> {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? host}`);\n\n if (req.method === \"GET\" && url.pathname === HEALTHZ_PATH) {\n res.writeHead(200, { \"Content-Type\": \"text/plain\" });\n res.end(\"ok\");\n return;\n }\n\n if (req.method === \"GET\" && url.pathname === SSE_PATH) {\n await handleSseConnect(res, sessions, logger);\n return;\n }\n\n if (req.method === \"POST\" && url.pathname === MESSAGES_PATH) {\n await handlePostMessage(req, res, url, sessions);\n return;\n }\n\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"not found\");\n}\n\n/**\n * Start an HTTP server that hosts the Directive knowledge MCP server\n * over SSE. Returns the underlying Node `http.Server` so callers can\n * `close()` it on shutdown.\n */\nexport async function startSseServer(\n options: SseServerOptions = {},\n): Promise<Server> {\n const port = options.port ?? 3000;\n const host = options.host ?? \"127.0.0.1\";\n const logger = options.logger ?? console;\n\n const sessions: Sessions = new Map();\n\n const httpServer = createServer(async (req, res) => {\n try {\n await routeRequest(req, res, sessions, logger, host);\n } catch (err) {\n logger.error(\"[directive-mcp] request error:\", err);\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"text/plain\" });\n }\n res.end(\"internal server error\");\n }\n });\n\n await new Promise<void>((resolve) => {\n httpServer.listen(port, host, () => {\n logger.log(\n `[directive-mcp] sse server listening at http://${host}:${port}${SSE_PATH}`,\n );\n resolve();\n });\n });\n\n return httpServer;\n}\n","/**\n * CLI entry for `@directive-run/mcp`.\n *\n * Default transport is stdio — the canonical MCP local-client\n * pattern (Claude Desktop, Cursor MCP, MCP Inspector). Pass `--sse`\n * to start the HTTP SSE server instead, for hosting the package\n * at `mcp.directive.run` or in a private MCP gateway.\n *\n * Usage:\n *\n * directive-mcp # stdio\n * directive-mcp --sse # SSE on 127.0.0.1:3000\n * directive-mcp --sse \\\n * --port 8080 --host 0.0.0.0 # SSE on 0.0.0.0:8080\n * directive-mcp --help\n * directive-mcp --version\n */\n\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { createDirectiveServer } from \"./server.js\";\nimport { startSseServer } from \"./sse.js\";\n\nconst VERSION = \"0.1.0\";\n\nconst USAGE = `directive-mcp — MCP server exposing Directive to AI clients\n\nUsage:\n directive-mcp Run stdio transport (default)\n directive-mcp --sse Run SSE transport on 127.0.0.1:3000\n directive-mcp --sse --port 8080 --host 0.0.0.0\n\nOptions:\n --sse Use HTTP SSE transport instead of stdio\n --port <port> SSE port (default: 3000)\n --host <host> SSE bind host (default: 127.0.0.1)\n --help, -h Show this help\n --version, -v Show package version\n\nDocs: https://directive.run/docs/ide-integration\n`;\n\ninterface ParsedArgs {\n sse: boolean;\n port: number;\n host: string;\n help: boolean;\n version: boolean;\n}\n\nfunction parseArgs(argv: string[]): ParsedArgs {\n const result: ParsedArgs = {\n sse: false,\n port: 3000,\n host: \"127.0.0.1\",\n help: false,\n version: false,\n };\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n switch (arg) {\n case \"--sse\":\n result.sse = true;\n break;\n case \"--port\": {\n const next = argv[++i];\n if (!next) {\n throw new Error(\"--port requires a value\");\n }\n const n = Number(next);\n if (!Number.isInteger(n) || n <= 0 || n > 65535) {\n throw new Error(`invalid --port: ${next}`);\n }\n result.port = n;\n break;\n }\n case \"--host\": {\n const next = argv[++i];\n if (!next) {\n throw new Error(\"--host requires a value\");\n }\n result.host = next;\n break;\n }\n case \"--help\":\n case \"-h\":\n result.help = true;\n break;\n case \"--version\":\n case \"-v\":\n result.version = true;\n break;\n default:\n throw new Error(`unknown argument: ${arg}`);\n }\n }\n\n return result;\n}\n\nasync function main(): Promise<void> {\n let args: ParsedArgs;\n try {\n args = parseArgs(process.argv.slice(2));\n } catch (err) {\n process.stderr.write(`${(err as Error).message}\\n\\n${USAGE}`);\n process.exit(2);\n }\n\n if (args.help) {\n process.stdout.write(USAGE);\n return;\n }\n if (args.version) {\n process.stdout.write(`${VERSION}\\n`);\n return;\n }\n\n if (args.sse) {\n const httpServer = await startSseServer({\n port: args.port,\n host: args.host,\n });\n\n const shutdown = () => {\n httpServer.close(() => process.exit(0));\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n return;\n }\n\n const server = createDirectiveServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nmain().catch((err: unknown) => {\n process.stderr.write(\n `[directive-mcp] fatal: ${(err as Error).stack ?? String(err)}\\n`,\n );\n process.exit(1);\n});\n"]}
package/dist/index.cjs ADDED
@@ -0,0 +1,22 @@
1
+ 'use strict';var claudePlugin=require('@directive-run/claude-plugin'),knowledge=require('@directive-run/knowledge'),mcp_js=require('@modelcontextprotocol/sdk/server/mcp.js'),zod=require('zod'),http=require('http'),sse_js=require('@modelcontextprotocol/sdk/server/sse.js');var k="0.1.0",g=50,m=200;function $(n){return n.length>m?`${n.slice(0,m)}\u2026`:n}function T(n,e){let t=n.toLowerCase(),s=[];for(let[i,r]of e){let a=r.split(`
2
+ `);for(let o=0;o<a.length;o++){let l=a[o];if(l.toLowerCase().includes(t)&&(s.push(`${i}.md:${o+1}: ${$(l)}`),s.length>=g))return s}}return s}function d(){let n=new mcp_js.McpServer({name:"directive",version:k});return n.registerTool("list_knowledge",{title:"List Directive knowledge files",description:"List every knowledge file shipped in @directive-run/knowledge. Returns the file names (without .md) that can be passed to get_knowledge. Covers core docs (engine, facts, constraints, resolvers, derivations, effects, plugins, modules, systems, testing) and AI docs (orchestrator, agents, adapters, guardrails, memory, MCP, RAG, security, evals, budget, multi-agent).",inputSchema:{}},async()=>{let e=knowledge.getAllKnowledge(),t=Array.from(e.keys()).sort();return {content:[{type:"text",text:`${t.length} knowledge files:
3
+ ${t.join(`
4
+ `)}`}]}}),n.registerTool("get_knowledge",{title:"Get a Directive knowledge file",description:"Fetch the full Markdown contents of one Directive knowledge file by name. Use list_knowledge first to discover available names. Names match the file stem (e.g. 'constraints', 'ai-orchestrator', 'api-skeleton').",inputSchema:{name:zod.z.string().min(1).describe("The knowledge file name (no .md suffix). Example: 'constraints', 'ai-orchestrator', 'api-skeleton'.")}},async({name:e})=>{let t=knowledge.getKnowledge(e);return t?{content:[{type:"text",text:t}]}:{isError:true,content:[{type:"text",text:`Knowledge file not found: '${e}'. Call list_knowledge to see available names.`}]}}),n.registerTool("list_examples",{title:"List Directive code examples",description:"List every code example shipped in @directive-run/knowledge. Examples are minimal, working TypeScript files demonstrating one concept each. Pass the returned names to get_example.",inputSchema:{}},async()=>{let e=knowledge.getAllExamples(),t=Array.from(e.keys()).sort();return {content:[{type:"text",text:`${t.length} examples:
5
+ ${t.join(`
6
+ `)}`}]}}),n.registerTool("get_example",{title:"Get a Directive code example",description:"Fetch the source of one Directive code example by name. Use list_examples first to discover available names. Returns raw TypeScript.",inputSchema:{name:zod.z.string().min(1).describe("The example file name (no .ts suffix). Example: 'basic-module', 'ai-orchestrator'.")}},async({name:e})=>{let t=knowledge.getExample(e);return t?{content:[{type:"text",text:`\`\`\`typescript
7
+ ${t}
8
+ \`\`\``}]}:{isError:true,content:[{type:"text",text:`Example not found: '${e}'. Call list_examples to see available names.`}]}}),n.registerTool("search_knowledge",{title:"Search Directive knowledge files",description:"Case-insensitive substring search across every knowledge file. Returns up to 50 matching lines with the file name and line context. Useful for discovering which knowledge file covers a topic before calling get_knowledge for the full document.",inputSchema:{query:zod.z.string().min(1).describe("The search string. Matched case-insensitively against every line of every knowledge file.")}},async({query:e})=>{let t=T(e,knowledge.getAllKnowledge());return t.length===0?{content:[{type:"text",text:`No matches for '${e}'.`}]}:{content:[{type:"text",text:`${t.length===g?`${t.length}+ matches (truncated):`:`${t.length} matches:`}
9
+ ${t.join(`
10
+ `)}`}]}}),n.registerTool("list_skills",{title:"List Directive Claude Code skills",description:"List every skill bundled in @directive-run/claude-plugin. Each skill is a gerund-named bundle of one SKILL.md plus supporting knowledge files. Pass a returned name to get_skill.",inputSchema:{}},async()=>{let e=claudePlugin.getAllSkills(),t=Array.from(e.keys()).sort();return {content:[{type:"text",text:`${t.length} skills:
11
+ ${t.join(`
12
+ `)}`}]}}),n.registerTool("get_skill",{title:"Get a Directive Claude Code skill",description:"Fetch one skill bundle: the SKILL.md manifest plus every supporting knowledge file concatenated into a single document. Use list_skills to discover names.",inputSchema:{name:zod.z.string().min(1).describe("The skill name (e.g. 'building-ai-orchestrators', 'writing-directive-constraints').")}},async({name:e})=>{let t=claudePlugin.getSkill(e);if(!t)return {isError:true,content:[{type:"text",text:`Skill not found: '${e}'. Call list_skills to see available names.`}]};let s=[`# Skill: ${t.name}
13
+
14
+ ${t.manifest}`];for(let[i,r]of t.files)s.push(`---
15
+
16
+ ## ${i}.md
17
+
18
+ ${r}`);return {content:[{type:"text",text:s.join(`
19
+
20
+ `)}]}}),n}var u="/messages",h="/sse",b="/healthz";async function C(n,e,t){let s=new sse_js.SSEServerTransport(u,n),i=d();e.set(s.sessionId,{transport:s});let r=()=>{e.delete(s.sessionId);};n.on("close",r),s.onclose=r,await i.connect(s),t.log(`[directive-mcp] sse session opened: ${s.sessionId}`);}async function L(n,e,t,s){let i=t.searchParams.get("sessionId");if(!i){e.writeHead(400,{"Content-Type":"text/plain"}),e.end("missing sessionId query parameter");return}let r=s.get(i);if(!r){e.writeHead(404,{"Content-Type":"text/plain"}),e.end(`unknown session: ${i}`);return}await r.transport.handlePostMessage(n,e);}async function P(n,e,t,s,i){let r=new URL(n.url??"/",`http://${n.headers.host??i}`);if(n.method==="GET"&&r.pathname===b){e.writeHead(200,{"Content-Type":"text/plain"}),e.end("ok");return}if(n.method==="GET"&&r.pathname===h){await C(e,t,s);return}if(n.method==="POST"&&r.pathname===u){await L(n,e,r,t);return}e.writeHead(404,{"Content-Type":"text/plain"}),e.end("not found");}async function A(n={}){let e=n.port??3e3,t=n.host??"127.0.0.1",s=n.logger??console,i=new Map,r=http.createServer(async(a,o)=>{try{await P(a,o,i,s,t);}catch(l){s.error("[directive-mcp] request error:",l),o.headersSent||o.writeHead(500,{"Content-Type":"text/plain"}),o.end("internal server error");}});return await new Promise(a=>{r.listen(e,t,()=>{s.log(`[directive-mcp] sse server listening at http://${t}:${e}${h}`),a();});}),r}
21
+ exports.createDirectiveServer=d;exports.startSseServer=A;//# sourceMappingURL=index.cjs.map
22
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts","../src/sse.ts"],"names":["PKG_VERSION","MAX_SEARCH_RESULTS","MAX_LINE_PREVIEW","formatLinePreview","line","collectSearchHits","query","knowledge","lowered","hits","name","content","lines","i","createDirectiveServer","server","McpServer","getAllKnowledge","names","z","getKnowledge","examples","getAllExamples","getExample","skills","getAllSkills","skill","getSkill","parts","fileName","MESSAGES_PATH","SSE_PATH","HEALTHZ_PATH","handleSseConnect","res","sessions","logger","transport","SSEServerTransport","cleanup","handlePostMessage","req","url","sessionId","session","routeRequest","host","startSseServer","options","port","httpServer","createServer","err","resolve"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Build a Model Context Protocol server that exposes Directive to MCP\n * clients. Transport-agnostic — call `createDirectiveServer()` to get a\n * configured `McpServer`, then connect it to a stdio transport (local\n * clients) or an SSE transport (hosted at `mcp.directive.run`).\n *\n * This is the SERVER side of the protocol. For the CLIENT side —\n * Directive AI agents calling out to external MCP servers — see\n * `@directive-run/ai/mcp` (`createMCPAdapter`).\n *\n * Current tool surface (knowledge + skill bundles):\n *\n * - `list_knowledge` — every knowledge file name (core + AI + skeleton).\n * - `get_knowledge` — read one knowledge file by name.\n * - `list_examples` — every example file name.\n * - `get_example` — read one example by name.\n * - `search_knowledge` — case-insensitive substring search across all\n * knowledge files, returning the file name + matching lines.\n * - `list_skills` — every Claude Code skill bundled in the plugin.\n * - `get_skill` — read one skill's `SKILL.md` plus its supporting\n * files as a single concatenated document.\n *\n * The package is the umbrella for \"Directive as an MCP server\" — future\n * additions can expose runtime introspection, system state, CLI\n * commands, and debug snapshots through the same binary.\n */\n\nimport { getAllSkills, getSkill } from \"@directive-run/claude-plugin\";\nimport {\n getAllExamples,\n getAllKnowledge,\n getExample,\n getKnowledge,\n} from \"@directive-run/knowledge\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\n\nconst PKG_VERSION = \"0.1.0\";\n\nconst MAX_SEARCH_RESULTS = 50;\nconst MAX_LINE_PREVIEW = 200;\n\nfunction formatLinePreview(line: string): string {\n return line.length > MAX_LINE_PREVIEW\n ? `${line.slice(0, MAX_LINE_PREVIEW)}…`\n : line;\n}\n\nfunction collectSearchHits(\n query: string,\n knowledge: ReadonlyMap<string, string>,\n): string[] {\n const lowered = query.toLowerCase();\n const hits: string[] = [];\n\n for (const [name, content] of knowledge) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n if (!line.toLowerCase().includes(lowered)) {\n continue;\n }\n hits.push(`${name}.md:${i + 1}: ${formatLinePreview(line)}`);\n if (hits.length >= MAX_SEARCH_RESULTS) {\n return hits;\n }\n }\n }\n\n return hits;\n}\n\n/**\n * Build the MCP server with every Directive tool registered.\n *\n * The returned server has no transport attached — connect it to\n * `StdioServerTransport` for local clients or an SSE transport for\n * hosted deployments.\n */\nexport function createDirectiveServer(): McpServer {\n const server = new McpServer({\n name: \"directive\",\n version: PKG_VERSION,\n });\n\n server.registerTool(\n \"list_knowledge\",\n {\n title: \"List Directive knowledge files\",\n description:\n \"List every knowledge file shipped in @directive-run/knowledge. Returns the file names (without .md) that can be passed to get_knowledge. Covers core docs (engine, facts, constraints, resolvers, derivations, effects, plugins, modules, systems, testing) and AI docs (orchestrator, agents, adapters, guardrails, memory, MCP, RAG, security, evals, budget, multi-agent).\",\n inputSchema: {},\n },\n async () => {\n const knowledge = getAllKnowledge();\n const names = Array.from(knowledge.keys()).sort();\n\n return {\n content: [\n {\n type: \"text\",\n text: `${names.length} knowledge files:\\n${names.join(\"\\n\")}`,\n },\n ],\n };\n },\n );\n\n server.registerTool(\n \"get_knowledge\",\n {\n title: \"Get a Directive knowledge file\",\n description:\n \"Fetch the full Markdown contents of one Directive knowledge file by name. Use list_knowledge first to discover available names. Names match the file stem (e.g. 'constraints', 'ai-orchestrator', 'api-skeleton').\",\n inputSchema: {\n name: z\n .string()\n .min(1)\n .describe(\n \"The knowledge file name (no .md suffix). Example: 'constraints', 'ai-orchestrator', 'api-skeleton'.\",\n ),\n },\n },\n async ({ name }) => {\n const content = getKnowledge(name);\n if (!content) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `Knowledge file not found: '${name}'. Call list_knowledge to see available names.`,\n },\n ],\n };\n }\n\n return {\n content: [{ type: \"text\", text: content }],\n };\n },\n );\n\n server.registerTool(\n \"list_examples\",\n {\n title: \"List Directive code examples\",\n description:\n \"List every code example shipped in @directive-run/knowledge. Examples are minimal, working TypeScript files demonstrating one concept each. Pass the returned names to get_example.\",\n inputSchema: {},\n },\n async () => {\n const examples = getAllExamples();\n const names = Array.from(examples.keys()).sort();\n\n return {\n content: [\n {\n type: \"text\",\n text: `${names.length} examples:\\n${names.join(\"\\n\")}`,\n },\n ],\n };\n },\n );\n\n server.registerTool(\n \"get_example\",\n {\n title: \"Get a Directive code example\",\n description:\n \"Fetch the source of one Directive code example by name. Use list_examples first to discover available names. Returns raw TypeScript.\",\n inputSchema: {\n name: z\n .string()\n .min(1)\n .describe(\n \"The example file name (no .ts suffix). Example: 'basic-module', 'ai-orchestrator'.\",\n ),\n },\n },\n async ({ name }) => {\n const content = getExample(name);\n if (!content) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `Example not found: '${name}'. Call list_examples to see available names.`,\n },\n ],\n };\n }\n\n return {\n content: [\n { type: \"text\", text: `\\`\\`\\`typescript\\n${content}\\n\\`\\`\\`` },\n ],\n };\n },\n );\n\n server.registerTool(\n \"search_knowledge\",\n {\n title: \"Search Directive knowledge files\",\n description:\n \"Case-insensitive substring search across every knowledge file. Returns up to 50 matching lines with the file name and line context. Useful for discovering which knowledge file covers a topic before calling get_knowledge for the full document.\",\n inputSchema: {\n query: z\n .string()\n .min(1)\n .describe(\n \"The search string. Matched case-insensitively against every line of every knowledge file.\",\n ),\n },\n },\n async ({ query }) => {\n const hits = collectSearchHits(query, getAllKnowledge());\n\n if (hits.length === 0) {\n return {\n content: [{ type: \"text\", text: `No matches for '${query}'.` }],\n };\n }\n\n const header =\n hits.length === MAX_SEARCH_RESULTS\n ? `${hits.length}+ matches (truncated):`\n : `${hits.length} matches:`;\n\n return {\n content: [{ type: \"text\", text: `${header}\\n${hits.join(\"\\n\")}` }],\n };\n },\n );\n\n server.registerTool(\n \"list_skills\",\n {\n title: \"List Directive Claude Code skills\",\n description:\n \"List every skill bundled in @directive-run/claude-plugin. Each skill is a gerund-named bundle of one SKILL.md plus supporting knowledge files. Pass a returned name to get_skill.\",\n inputSchema: {},\n },\n async () => {\n const skills = getAllSkills();\n const names = Array.from(skills.keys()).sort();\n\n return {\n content: [\n {\n type: \"text\",\n text: `${names.length} skills:\\n${names.join(\"\\n\")}`,\n },\n ],\n };\n },\n );\n\n server.registerTool(\n \"get_skill\",\n {\n title: \"Get a Directive Claude Code skill\",\n description:\n \"Fetch one skill bundle: the SKILL.md manifest plus every supporting knowledge file concatenated into a single document. Use list_skills to discover names.\",\n inputSchema: {\n name: z\n .string()\n .min(1)\n .describe(\n \"The skill name (e.g. 'building-ai-orchestrators', 'writing-directive-constraints').\",\n ),\n },\n },\n async ({ name }) => {\n const skill = getSkill(name);\n if (!skill) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `Skill not found: '${name}'. Call list_skills to see available names.`,\n },\n ],\n };\n }\n\n const parts = [`# Skill: ${skill.name}\\n\\n${skill.manifest}`];\n for (const [fileName, content] of skill.files) {\n parts.push(`---\\n\\n## ${fileName}.md\\n\\n${content}`);\n }\n\n return {\n content: [{ type: \"text\", text: parts.join(\"\\n\\n\") }],\n };\n },\n );\n\n return server;\n}\n","/**\n * SSE (Server-Sent Events) transport for the Directive knowledge MCP\n * server. Hosts the server over HTTP so remote MCP clients\n * (production AI agents, the docs site, third-party tools) can query\n * it at retrieval time — borrowed pattern from zustand's\n * `docs.pmnd.rs/api/sse`.\n *\n * One SSE connection per client. Each connection gets its own\n * MCP server instance so per-session state (subscriptions, list\n * cursors) stays isolated.\n *\n * Endpoints:\n *\n * - `GET /sse` — establish the SSE stream. Server sends a session\n * ID and the client connects `/messages?sessionId=…` for the\n * POST half of the channel.\n * - `POST /messages?sessionId=…` — client→server JSON-RPC messages.\n * - `GET /healthz` — liveness probe returning 200 + `ok`.\n */\n\nimport { type IncomingMessage, type Server, createServer } from \"node:http\";\nimport { SSEServerTransport } from \"@modelcontextprotocol/sdk/server/sse.js\";\nimport { createDirectiveServer } from \"./server.js\";\n\nconst MESSAGES_PATH = \"/messages\";\nconst SSE_PATH = \"/sse\";\nconst HEALTHZ_PATH = \"/healthz\";\n\nexport interface SseServerOptions {\n /** Port to listen on. Default: 3000. */\n port?: number;\n /** Host to bind to. Default: `127.0.0.1`. Use `0.0.0.0` for public deployments. */\n host?: string;\n /** Optional logger. Default: `console`. */\n logger?: Pick<Console, \"log\" | \"warn\" | \"error\">;\n}\n\ninterface ActiveSession {\n transport: SSEServerTransport;\n}\n\ntype Sessions = Map<string, ActiveSession>;\ntype Logger = Pick<Console, \"log\" | \"warn\" | \"error\">;\n\nasync function handleSseConnect(\n res: import(\"node:http\").ServerResponse,\n sessions: Sessions,\n logger: Logger,\n): Promise<void> {\n const transport = new SSEServerTransport(MESSAGES_PATH, res);\n const server = createDirectiveServer();\n sessions.set(transport.sessionId, { transport });\n\n const cleanup = () => {\n sessions.delete(transport.sessionId);\n };\n res.on(\"close\", cleanup);\n transport.onclose = cleanup;\n\n await server.connect(transport);\n logger.log(`[directive-mcp] sse session opened: ${transport.sessionId}`);\n}\n\nasync function handlePostMessage(\n req: IncomingMessage,\n res: import(\"node:http\").ServerResponse,\n url: URL,\n sessions: Sessions,\n): Promise<void> {\n const sessionId = url.searchParams.get(\"sessionId\");\n if (!sessionId) {\n res.writeHead(400, { \"Content-Type\": \"text/plain\" });\n res.end(\"missing sessionId query parameter\");\n return;\n }\n\n const session = sessions.get(sessionId);\n if (!session) {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(`unknown session: ${sessionId}`);\n return;\n }\n\n await session.transport.handlePostMessage(req, res);\n}\n\nasync function routeRequest(\n req: IncomingMessage,\n res: import(\"node:http\").ServerResponse,\n sessions: Sessions,\n logger: Logger,\n host: string,\n): Promise<void> {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? host}`);\n\n if (req.method === \"GET\" && url.pathname === HEALTHZ_PATH) {\n res.writeHead(200, { \"Content-Type\": \"text/plain\" });\n res.end(\"ok\");\n return;\n }\n\n if (req.method === \"GET\" && url.pathname === SSE_PATH) {\n await handleSseConnect(res, sessions, logger);\n return;\n }\n\n if (req.method === \"POST\" && url.pathname === MESSAGES_PATH) {\n await handlePostMessage(req, res, url, sessions);\n return;\n }\n\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"not found\");\n}\n\n/**\n * Start an HTTP server that hosts the Directive knowledge MCP server\n * over SSE. Returns the underlying Node `http.Server` so callers can\n * `close()` it on shutdown.\n */\nexport async function startSseServer(\n options: SseServerOptions = {},\n): Promise<Server> {\n const port = options.port ?? 3000;\n const host = options.host ?? \"127.0.0.1\";\n const logger = options.logger ?? console;\n\n const sessions: Sessions = new Map();\n\n const httpServer = createServer(async (req, res) => {\n try {\n await routeRequest(req, res, sessions, logger, host);\n } catch (err) {\n logger.error(\"[directive-mcp] request error:\", err);\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"text/plain\" });\n }\n res.end(\"internal server error\");\n }\n });\n\n await new Promise<void>((resolve) => {\n httpServer.listen(port, host, () => {\n logger.log(\n `[directive-mcp] sse server listening at http://${host}:${port}${SSE_PATH}`,\n );\n resolve();\n });\n });\n\n return httpServer;\n}\n"]}
@@ -0,0 +1,75 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { Server } from 'node:http';
3
+
4
+ /**
5
+ * Build a Model Context Protocol server that exposes Directive to MCP
6
+ * clients. Transport-agnostic — call `createDirectiveServer()` to get a
7
+ * configured `McpServer`, then connect it to a stdio transport (local
8
+ * clients) or an SSE transport (hosted at `mcp.directive.run`).
9
+ *
10
+ * This is the SERVER side of the protocol. For the CLIENT side —
11
+ * Directive AI agents calling out to external MCP servers — see
12
+ * `@directive-run/ai/mcp` (`createMCPAdapter`).
13
+ *
14
+ * Current tool surface (knowledge + skill bundles):
15
+ *
16
+ * - `list_knowledge` — every knowledge file name (core + AI + skeleton).
17
+ * - `get_knowledge` — read one knowledge file by name.
18
+ * - `list_examples` — every example file name.
19
+ * - `get_example` — read one example by name.
20
+ * - `search_knowledge` — case-insensitive substring search across all
21
+ * knowledge files, returning the file name + matching lines.
22
+ * - `list_skills` — every Claude Code skill bundled in the plugin.
23
+ * - `get_skill` — read one skill's `SKILL.md` plus its supporting
24
+ * files as a single concatenated document.
25
+ *
26
+ * The package is the umbrella for "Directive as an MCP server" — future
27
+ * additions can expose runtime introspection, system state, CLI
28
+ * commands, and debug snapshots through the same binary.
29
+ */
30
+
31
+ /**
32
+ * Build the MCP server with every Directive tool registered.
33
+ *
34
+ * The returned server has no transport attached — connect it to
35
+ * `StdioServerTransport` for local clients or an SSE transport for
36
+ * hosted deployments.
37
+ */
38
+ declare function createDirectiveServer(): McpServer;
39
+
40
+ /**
41
+ * SSE (Server-Sent Events) transport for the Directive knowledge MCP
42
+ * server. Hosts the server over HTTP so remote MCP clients
43
+ * (production AI agents, the docs site, third-party tools) can query
44
+ * it at retrieval time — borrowed pattern from zustand's
45
+ * `docs.pmnd.rs/api/sse`.
46
+ *
47
+ * One SSE connection per client. Each connection gets its own
48
+ * MCP server instance so per-session state (subscriptions, list
49
+ * cursors) stays isolated.
50
+ *
51
+ * Endpoints:
52
+ *
53
+ * - `GET /sse` — establish the SSE stream. Server sends a session
54
+ * ID and the client connects `/messages?sessionId=…` for the
55
+ * POST half of the channel.
56
+ * - `POST /messages?sessionId=…` — client→server JSON-RPC messages.
57
+ * - `GET /healthz` — liveness probe returning 200 + `ok`.
58
+ */
59
+
60
+ interface SseServerOptions {
61
+ /** Port to listen on. Default: 3000. */
62
+ port?: number;
63
+ /** Host to bind to. Default: `127.0.0.1`. Use `0.0.0.0` for public deployments. */
64
+ host?: string;
65
+ /** Optional logger. Default: `console`. */
66
+ logger?: Pick<Console, "log" | "warn" | "error">;
67
+ }
68
+ /**
69
+ * Start an HTTP server that hosts the Directive knowledge MCP server
70
+ * over SSE. Returns the underlying Node `http.Server` so callers can
71
+ * `close()` it on shutdown.
72
+ */
73
+ declare function startSseServer(options?: SseServerOptions): Promise<Server>;
74
+
75
+ export { type SseServerOptions, createDirectiveServer, startSseServer };
@@ -0,0 +1,75 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { Server } from 'node:http';
3
+
4
+ /**
5
+ * Build a Model Context Protocol server that exposes Directive to MCP
6
+ * clients. Transport-agnostic — call `createDirectiveServer()` to get a
7
+ * configured `McpServer`, then connect it to a stdio transport (local
8
+ * clients) or an SSE transport (hosted at `mcp.directive.run`).
9
+ *
10
+ * This is the SERVER side of the protocol. For the CLIENT side —
11
+ * Directive AI agents calling out to external MCP servers — see
12
+ * `@directive-run/ai/mcp` (`createMCPAdapter`).
13
+ *
14
+ * Current tool surface (knowledge + skill bundles):
15
+ *
16
+ * - `list_knowledge` — every knowledge file name (core + AI + skeleton).
17
+ * - `get_knowledge` — read one knowledge file by name.
18
+ * - `list_examples` — every example file name.
19
+ * - `get_example` — read one example by name.
20
+ * - `search_knowledge` — case-insensitive substring search across all
21
+ * knowledge files, returning the file name + matching lines.
22
+ * - `list_skills` — every Claude Code skill bundled in the plugin.
23
+ * - `get_skill` — read one skill's `SKILL.md` plus its supporting
24
+ * files as a single concatenated document.
25
+ *
26
+ * The package is the umbrella for "Directive as an MCP server" — future
27
+ * additions can expose runtime introspection, system state, CLI
28
+ * commands, and debug snapshots through the same binary.
29
+ */
30
+
31
+ /**
32
+ * Build the MCP server with every Directive tool registered.
33
+ *
34
+ * The returned server has no transport attached — connect it to
35
+ * `StdioServerTransport` for local clients or an SSE transport for
36
+ * hosted deployments.
37
+ */
38
+ declare function createDirectiveServer(): McpServer;
39
+
40
+ /**
41
+ * SSE (Server-Sent Events) transport for the Directive knowledge MCP
42
+ * server. Hosts the server over HTTP so remote MCP clients
43
+ * (production AI agents, the docs site, third-party tools) can query
44
+ * it at retrieval time — borrowed pattern from zustand's
45
+ * `docs.pmnd.rs/api/sse`.
46
+ *
47
+ * One SSE connection per client. Each connection gets its own
48
+ * MCP server instance so per-session state (subscriptions, list
49
+ * cursors) stays isolated.
50
+ *
51
+ * Endpoints:
52
+ *
53
+ * - `GET /sse` — establish the SSE stream. Server sends a session
54
+ * ID and the client connects `/messages?sessionId=…` for the
55
+ * POST half of the channel.
56
+ * - `POST /messages?sessionId=…` — client→server JSON-RPC messages.
57
+ * - `GET /healthz` — liveness probe returning 200 + `ok`.
58
+ */
59
+
60
+ interface SseServerOptions {
61
+ /** Port to listen on. Default: 3000. */
62
+ port?: number;
63
+ /** Host to bind to. Default: `127.0.0.1`. Use `0.0.0.0` for public deployments. */
64
+ host?: string;
65
+ /** Optional logger. Default: `console`. */
66
+ logger?: Pick<Console, "log" | "warn" | "error">;
67
+ }
68
+ /**
69
+ * Start an HTTP server that hosts the Directive knowledge MCP server
70
+ * over SSE. Returns the underlying Node `http.Server` so callers can
71
+ * `close()` it on shutdown.
72
+ */
73
+ declare function startSseServer(options?: SseServerOptions): Promise<Server>;
74
+
75
+ export { type SseServerOptions, createDirectiveServer, startSseServer };
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ import {getAllSkills,getSkill}from'@directive-run/claude-plugin';import {getAllKnowledge,getKnowledge,getAllExamples,getExample}from'@directive-run/knowledge';import {McpServer}from'@modelcontextprotocol/sdk/server/mcp.js';import {z}from'zod';import {createServer}from'http';import {SSEServerTransport}from'@modelcontextprotocol/sdk/server/sse.js';var k="0.1.0",g=50,m=200;function $(n){return n.length>m?`${n.slice(0,m)}\u2026`:n}function T(n,e){let t=n.toLowerCase(),s=[];for(let[i,r]of e){let a=r.split(`
2
+ `);for(let o=0;o<a.length;o++){let l=a[o];if(l.toLowerCase().includes(t)&&(s.push(`${i}.md:${o+1}: ${$(l)}`),s.length>=g))return s}}return s}function d(){let n=new McpServer({name:"directive",version:k});return n.registerTool("list_knowledge",{title:"List Directive knowledge files",description:"List every knowledge file shipped in @directive-run/knowledge. Returns the file names (without .md) that can be passed to get_knowledge. Covers core docs (engine, facts, constraints, resolvers, derivations, effects, plugins, modules, systems, testing) and AI docs (orchestrator, agents, adapters, guardrails, memory, MCP, RAG, security, evals, budget, multi-agent).",inputSchema:{}},async()=>{let e=getAllKnowledge(),t=Array.from(e.keys()).sort();return {content:[{type:"text",text:`${t.length} knowledge files:
3
+ ${t.join(`
4
+ `)}`}]}}),n.registerTool("get_knowledge",{title:"Get a Directive knowledge file",description:"Fetch the full Markdown contents of one Directive knowledge file by name. Use list_knowledge first to discover available names. Names match the file stem (e.g. 'constraints', 'ai-orchestrator', 'api-skeleton').",inputSchema:{name:z.string().min(1).describe("The knowledge file name (no .md suffix). Example: 'constraints', 'ai-orchestrator', 'api-skeleton'.")}},async({name:e})=>{let t=getKnowledge(e);return t?{content:[{type:"text",text:t}]}:{isError:true,content:[{type:"text",text:`Knowledge file not found: '${e}'. Call list_knowledge to see available names.`}]}}),n.registerTool("list_examples",{title:"List Directive code examples",description:"List every code example shipped in @directive-run/knowledge. Examples are minimal, working TypeScript files demonstrating one concept each. Pass the returned names to get_example.",inputSchema:{}},async()=>{let e=getAllExamples(),t=Array.from(e.keys()).sort();return {content:[{type:"text",text:`${t.length} examples:
5
+ ${t.join(`
6
+ `)}`}]}}),n.registerTool("get_example",{title:"Get a Directive code example",description:"Fetch the source of one Directive code example by name. Use list_examples first to discover available names. Returns raw TypeScript.",inputSchema:{name:z.string().min(1).describe("The example file name (no .ts suffix). Example: 'basic-module', 'ai-orchestrator'.")}},async({name:e})=>{let t=getExample(e);return t?{content:[{type:"text",text:`\`\`\`typescript
7
+ ${t}
8
+ \`\`\``}]}:{isError:true,content:[{type:"text",text:`Example not found: '${e}'. Call list_examples to see available names.`}]}}),n.registerTool("search_knowledge",{title:"Search Directive knowledge files",description:"Case-insensitive substring search across every knowledge file. Returns up to 50 matching lines with the file name and line context. Useful for discovering which knowledge file covers a topic before calling get_knowledge for the full document.",inputSchema:{query:z.string().min(1).describe("The search string. Matched case-insensitively against every line of every knowledge file.")}},async({query:e})=>{let t=T(e,getAllKnowledge());return t.length===0?{content:[{type:"text",text:`No matches for '${e}'.`}]}:{content:[{type:"text",text:`${t.length===g?`${t.length}+ matches (truncated):`:`${t.length} matches:`}
9
+ ${t.join(`
10
+ `)}`}]}}),n.registerTool("list_skills",{title:"List Directive Claude Code skills",description:"List every skill bundled in @directive-run/claude-plugin. Each skill is a gerund-named bundle of one SKILL.md plus supporting knowledge files. Pass a returned name to get_skill.",inputSchema:{}},async()=>{let e=getAllSkills(),t=Array.from(e.keys()).sort();return {content:[{type:"text",text:`${t.length} skills:
11
+ ${t.join(`
12
+ `)}`}]}}),n.registerTool("get_skill",{title:"Get a Directive Claude Code skill",description:"Fetch one skill bundle: the SKILL.md manifest plus every supporting knowledge file concatenated into a single document. Use list_skills to discover names.",inputSchema:{name:z.string().min(1).describe("The skill name (e.g. 'building-ai-orchestrators', 'writing-directive-constraints').")}},async({name:e})=>{let t=getSkill(e);if(!t)return {isError:true,content:[{type:"text",text:`Skill not found: '${e}'. Call list_skills to see available names.`}]};let s=[`# Skill: ${t.name}
13
+
14
+ ${t.manifest}`];for(let[i,r]of t.files)s.push(`---
15
+
16
+ ## ${i}.md
17
+
18
+ ${r}`);return {content:[{type:"text",text:s.join(`
19
+
20
+ `)}]}}),n}var u="/messages",h="/sse",b="/healthz";async function C(n,e,t){let s=new SSEServerTransport(u,n),i=d();e.set(s.sessionId,{transport:s});let r=()=>{e.delete(s.sessionId);};n.on("close",r),s.onclose=r,await i.connect(s),t.log(`[directive-mcp] sse session opened: ${s.sessionId}`);}async function L(n,e,t,s){let i=t.searchParams.get("sessionId");if(!i){e.writeHead(400,{"Content-Type":"text/plain"}),e.end("missing sessionId query parameter");return}let r=s.get(i);if(!r){e.writeHead(404,{"Content-Type":"text/plain"}),e.end(`unknown session: ${i}`);return}await r.transport.handlePostMessage(n,e);}async function P(n,e,t,s,i){let r=new URL(n.url??"/",`http://${n.headers.host??i}`);if(n.method==="GET"&&r.pathname===b){e.writeHead(200,{"Content-Type":"text/plain"}),e.end("ok");return}if(n.method==="GET"&&r.pathname===h){await C(e,t,s);return}if(n.method==="POST"&&r.pathname===u){await L(n,e,r,t);return}e.writeHead(404,{"Content-Type":"text/plain"}),e.end("not found");}async function A(n={}){let e=n.port??3e3,t=n.host??"127.0.0.1",s=n.logger??console,i=new Map,r=createServer(async(a,o)=>{try{await P(a,o,i,s,t);}catch(l){s.error("[directive-mcp] request error:",l),o.headersSent||o.writeHead(500,{"Content-Type":"text/plain"}),o.end("internal server error");}});return await new Promise(a=>{r.listen(e,t,()=>{s.log(`[directive-mcp] sse server listening at http://${t}:${e}${h}`),a();});}),r}
21
+ export{d as createDirectiveServer,A as startSseServer};//# sourceMappingURL=index.js.map
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts","../src/sse.ts"],"names":["PKG_VERSION","MAX_SEARCH_RESULTS","MAX_LINE_PREVIEW","formatLinePreview","line","collectSearchHits","query","knowledge","lowered","hits","name","content","lines","i","createDirectiveServer","server","McpServer","getAllKnowledge","names","z","getKnowledge","examples","getAllExamples","getExample","skills","getAllSkills","skill","getSkill","parts","fileName","MESSAGES_PATH","SSE_PATH","HEALTHZ_PATH","handleSseConnect","res","sessions","logger","transport","SSEServerTransport","cleanup","handlePostMessage","req","url","sessionId","session","routeRequest","host","startSseServer","options","port","httpServer","createServer","err","resolve"],"mappings":"4VAqCA,IAAMA,CAAAA,CAAc,OAAA,CAEdC,CAAAA,CAAqB,GACrBC,CAAAA,CAAmB,GAAA,CAEzB,SAASC,CAAAA,CAAkBC,EAAsB,CAC/C,OAAOA,CAAAA,CAAK,MAAA,CAASF,CAAAA,CACjB,CAAA,EAAGE,CAAAA,CAAK,KAAA,CAAM,EAAGF,CAAgB,CAAC,CAAA,MAAA,CAAA,CAClCE,CACN,CAEA,SAASC,CAAAA,CACPC,CAAAA,CACAC,EACU,CACV,IAAMC,CAAAA,CAAUF,CAAAA,CAAM,WAAA,EAAY,CAC5BG,CAAAA,CAAiB,GAEvB,IAAA,GAAW,CAACC,CAAAA,CAAMC,CAAO,IAAKJ,CAAAA,CAAW,CACvC,IAAMK,CAAAA,CAAQD,EAAQ,KAAA,CAAM;AAAA,CAAI,CAAA,CAChC,QAASE,CAAAA,CAAI,CAAA,CAAGA,EAAID,CAAAA,CAAM,MAAA,CAAQC,IAAK,CACrC,IAAMT,EAAOQ,CAAAA,CAAMC,CAAC,EACpB,GAAKT,CAAAA,CAAK,aAAY,CAAE,QAAA,CAASI,CAAO,CAAA,GAGxCC,CAAAA,CAAK,IAAA,CAAK,GAAGC,CAAI,CAAA,IAAA,EAAOG,EAAI,CAAC,CAAA,EAAA,EAAKV,EAAkBC,CAAI,CAAC,EAAE,CAAA,CACvDK,CAAAA,CAAK,QAAUR,CAAAA,CAAAA,CACjB,OAAOQ,CAEX,CACF,CAEA,OAAOA,CACT,CASO,SAASK,CAAAA,EAAmC,CACjD,IAAMC,EAAS,IAAIC,SAAAA,CAAU,CAC3B,IAAA,CAAM,WAAA,CACN,QAAShB,CACX,CAAC,EAED,OAAAe,CAAAA,CAAO,aACL,gBAAA,CACA,CACE,MAAO,gCAAA,CACP,WAAA,CACE,gXACF,WAAA,CAAa,EACf,CAAA,CACA,SAAY,CACV,IAAMR,CAAAA,CAAYU,eAAAA,GACZC,CAAAA,CAAQ,KAAA,CAAM,KAAKX,CAAAA,CAAU,IAAA,EAAM,CAAA,CAAE,IAAA,GAE3C,OAAO,CACL,QAAS,CACP,CACE,KAAM,MAAA,CACN,IAAA,CAAM,CAAA,EAAGW,CAAAA,CAAM,MAAM,CAAA;AAAA,EAAsBA,EAAM,IAAA,CAAK;AAAA,CAAI,CAAC,EAC7D,CACF,CACF,CACF,CACF,CAAA,CAEAH,CAAAA,CAAO,YAAA,CACL,eAAA,CACA,CACE,MAAO,gCAAA,CACP,WAAA,CACE,oNAAA,CACF,WAAA,CAAa,CACX,IAAA,CAAMI,EACH,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,QAAA,CACC,qGACF,CACJ,CACF,CAAA,CACA,MAAO,CAAE,IAAA,CAAAT,CAAK,CAAA,GAAM,CAClB,IAAMC,CAAAA,CAAUS,YAAAA,CAAaV,CAAI,EACjC,OAAKC,CAAAA,CAYE,CACL,OAAA,CAAS,CAAC,CAAE,KAAM,MAAA,CAAQ,IAAA,CAAMA,CAAQ,CAAC,CAC3C,CAAA,CAbS,CACL,OAAA,CAAS,IAAA,CACT,OAAA,CAAS,CACP,CACE,IAAA,CAAM,OACN,IAAA,CAAM,CAAA,2BAAA,EAA8BD,CAAI,CAAA,8CAAA,CAC1C,CACF,CACF,CAMJ,CACF,CAAA,CAEAK,CAAAA,CAAO,YAAA,CACL,eAAA,CACA,CACE,MAAO,8BAAA,CACP,WAAA,CACE,qLAAA,CACF,WAAA,CAAa,EACf,EACA,SAAY,CACV,IAAMM,CAAAA,CAAWC,cAAAA,EAAe,CAC1BJ,EAAQ,KAAA,CAAM,IAAA,CAAKG,CAAAA,CAAS,IAAA,EAAM,CAAA,CAAE,MAAK,CAE/C,OAAO,CACL,OAAA,CAAS,CACP,CACE,KAAM,MAAA,CACN,IAAA,CAAM,CAAA,EAAGH,CAAAA,CAAM,MAAM,CAAA;AAAA,EAAeA,EAAM,IAAA,CAAK;AAAA,CAAI,CAAC,EACtD,CACF,CACF,CACF,CACF,CAAA,CAEAH,CAAAA,CAAO,YAAA,CACL,aAAA,CACA,CACE,MAAO,8BAAA,CACP,WAAA,CACE,uIACF,WAAA,CAAa,CACX,KAAMI,CAAAA,CACH,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,SACC,oFACF,CACJ,CACF,CAAA,CACA,MAAO,CAAE,IAAA,CAAAT,CAAK,CAAA,GAAM,CAClB,IAAMC,CAAAA,CAAUY,WAAWb,CAAI,CAAA,CAC/B,OAAKC,CAAAA,CAYE,CACL,QAAS,CACP,CAAE,IAAA,CAAM,MAAA,CAAQ,IAAA,CAAM,CAAA;AAAA,EAAqBA,CAAO;AAAA,MAAA,CAAW,CAC/D,CACF,CAAA,CAfS,CACL,OAAA,CAAS,IAAA,CACT,QAAS,CACP,CACE,KAAM,MAAA,CACN,IAAA,CAAM,uBAAuBD,CAAI,CAAA,6CAAA,CACnC,CACF,CACF,CAQJ,CACF,CAAA,CAEAK,CAAAA,CAAO,aACL,kBAAA,CACA,CACE,MAAO,kCAAA,CACP,WAAA,CACE,qPACF,WAAA,CAAa,CACX,MAAOI,CAAAA,CACJ,MAAA,GACA,GAAA,CAAI,CAAC,EACL,QAAA,CACC,2FACF,CACJ,CACF,CAAA,CACA,MAAO,CAAE,KAAA,CAAAb,CAAM,CAAA,GAAM,CACnB,IAAMG,CAAAA,CAAOJ,EAAkBC,CAAAA,CAAOW,eAAAA,EAAiB,CAAA,CAEvD,OAAIR,EAAK,MAAA,GAAW,CAAA,CACX,CACL,OAAA,CAAS,CAAC,CAAE,IAAA,CAAM,MAAA,CAAQ,KAAM,CAAA,gBAAA,EAAmBH,CAAK,IAAK,CAAC,CAChE,EAQK,CACL,OAAA,CAAS,CAAC,CAAE,IAAA,CAAM,OAAQ,IAAA,CAAM,CAAA,EALhCG,EAAK,MAAA,GAAWR,CAAAA,CACZ,GAAGQ,CAAAA,CAAK,MAAM,yBACd,CAAA,EAAGA,CAAAA,CAAK,MAAM,CAAA,SAAA,CAGuB;AAAA,EAAKA,EAAK,IAAA,CAAK;AAAA,CAAI,CAAC,EAAG,CAAC,CACnE,CACF,CACF,CAAA,CAEAM,EAAO,YAAA,CACL,aAAA,CACA,CACE,KAAA,CAAO,mCAAA,CACP,YACE,mLAAA,CACF,WAAA,CAAa,EACf,CAAA,CACA,SAAY,CACV,IAAMS,EAASC,YAAAA,EAAa,CACtBP,EAAQ,KAAA,CAAM,IAAA,CAAKM,EAAO,IAAA,EAAM,EAAE,IAAA,EAAK,CAE7C,OAAO,CACL,OAAA,CAAS,CACP,CACE,IAAA,CAAM,OACN,IAAA,CAAM,CAAA,EAAGN,EAAM,MAAM,CAAA;AAAA,EAAaA,EAAM,IAAA,CAAK;AAAA,CAAI,CAAC,CAAA,CACpD,CACF,CACF,CACF,CACF,CAAA,CAEAH,CAAAA,CAAO,YAAA,CACL,WAAA,CACA,CACE,KAAA,CAAO,oCACP,WAAA,CACE,4JAAA,CACF,WAAA,CAAa,CACX,IAAA,CAAMI,CAAAA,CACH,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,QAAA,CACC,qFACF,CACJ,CACF,CAAA,CACA,MAAO,CAAE,IAAA,CAAAT,CAAK,CAAA,GAAM,CAClB,IAAMgB,CAAAA,CAAQC,QAAAA,CAASjB,CAAI,CAAA,CAC3B,GAAI,CAACgB,CAAAA,CACH,OAAO,CACL,OAAA,CAAS,KACT,OAAA,CAAS,CACP,CACE,IAAA,CAAM,MAAA,CACN,IAAA,CAAM,CAAA,kBAAA,EAAqBhB,CAAI,CAAA,2CAAA,CACjC,CACF,CACF,CAAA,CAGF,IAAMkB,CAAAA,CAAQ,CAAC,CAAA,SAAA,EAAYF,EAAM,IAAI;;AAAA,EAAOA,CAAAA,CAAM,QAAQ,CAAA,CAAE,CAAA,CAC5D,IAAA,GAAW,CAACG,CAAAA,CAAUlB,CAAO,CAAA,GAAKe,CAAAA,CAAM,KAAA,CACtCE,CAAAA,CAAM,IAAA,CAAK,CAAA;;AAAA,GAAA,EAAaC,CAAQ,CAAA;;AAAA,EAAUlB,CAAO,CAAA,CAAE,CAAA,CAGrD,OAAO,CACL,OAAA,CAAS,CAAC,CAAE,IAAA,CAAM,MAAA,CAAQ,IAAA,CAAMiB,CAAAA,CAAM,IAAA,CAAK;;AAAA,CAAM,CAAE,CAAC,CACtD,CACF,CACF,EAEOb,CACT,CCtRA,IAAMe,CAAAA,CAAgB,WAAA,CAChBC,CAAAA,CAAW,OACXC,CAAAA,CAAe,UAAA,CAkBrB,eAAeC,CAAAA,CACbC,EACAC,CAAAA,CACAC,CAAAA,CACe,CACf,IAAMC,EAAY,IAAIC,kBAAAA,CAAmBR,EAAeI,CAAG,CAAA,CACrDnB,EAASD,CAAAA,EAAsB,CACrCqB,CAAAA,CAAS,GAAA,CAAIE,EAAU,SAAA,CAAW,CAAE,SAAA,CAAAA,CAAU,CAAC,CAAA,CAE/C,IAAME,CAAAA,CAAU,IAAM,CACpBJ,CAAAA,CAAS,MAAA,CAAOE,CAAAA,CAAU,SAAS,EACrC,CAAA,CACAH,CAAAA,CAAI,EAAA,CAAG,OAAA,CAASK,CAAO,CAAA,CACvBF,CAAAA,CAAU,OAAA,CAAUE,CAAAA,CAEpB,MAAMxB,CAAAA,CAAO,OAAA,CAAQsB,CAAS,CAAA,CAC9BD,EAAO,GAAA,CAAI,CAAA,oCAAA,EAAuCC,EAAU,SAAS,CAAA,CAAE,EACzE,CAEA,eAAeG,CAAAA,CACbC,CAAAA,CACAP,EACAQ,CAAAA,CACAP,CAAAA,CACe,CACf,IAAMQ,EAAYD,CAAAA,CAAI,YAAA,CAAa,GAAA,CAAI,WAAW,EAClD,GAAI,CAACC,CAAAA,CAAW,CACdT,EAAI,SAAA,CAAU,GAAA,CAAK,CAAE,cAAA,CAAgB,YAAa,CAAC,CAAA,CACnDA,CAAAA,CAAI,GAAA,CAAI,mCAAmC,CAAA,CAC3C,MACF,CAEA,IAAMU,EAAUT,CAAAA,CAAS,GAAA,CAAIQ,CAAS,CAAA,CACtC,GAAI,CAACC,CAAAA,CAAS,CACZV,CAAAA,CAAI,SAAA,CAAU,IAAK,CAAE,cAAA,CAAgB,YAAa,CAAC,EACnDA,CAAAA,CAAI,GAAA,CAAI,CAAA,iBAAA,EAAoBS,CAAS,EAAE,CAAA,CACvC,MACF,CAEA,MAAMC,EAAQ,SAAA,CAAU,iBAAA,CAAkBH,CAAAA,CAAKP,CAAG,EACpD,CAEA,eAAeW,CAAAA,CACbJ,CAAAA,CACAP,EACAC,CAAAA,CACAC,CAAAA,CACAU,CAAAA,CACe,CACf,IAAMJ,CAAAA,CAAM,IAAI,IAAID,CAAAA,CAAI,GAAA,EAAO,IAAK,CAAA,OAAA,EAAUA,CAAAA,CAAI,OAAA,CAAQ,IAAA,EAAQK,CAAI,CAAA,CAAE,CAAA,CAExE,GAAIL,CAAAA,CAAI,SAAW,KAAA,EAASC,CAAAA,CAAI,QAAA,GAAaV,CAAAA,CAAc,CACzDE,CAAAA,CAAI,SAAA,CAAU,GAAA,CAAK,CAAE,eAAgB,YAAa,CAAC,CAAA,CACnDA,CAAAA,CAAI,IAAI,IAAI,CAAA,CACZ,MACF,CAEA,GAAIO,CAAAA,CAAI,MAAA,GAAW,KAAA,EAASC,CAAAA,CAAI,WAAaX,CAAAA,CAAU,CACrD,MAAME,CAAAA,CAAiBC,CAAAA,CAAKC,EAAUC,CAAM,CAAA,CAC5C,MACF,CAEA,GAAIK,CAAAA,CAAI,MAAA,GAAW,MAAA,EAAUC,CAAAA,CAAI,WAAaZ,CAAAA,CAAe,CAC3D,MAAMU,CAAAA,CAAkBC,EAAKP,CAAAA,CAAKQ,CAAAA,CAAKP,CAAQ,CAAA,CAC/C,MACF,CAEAD,CAAAA,CAAI,SAAA,CAAU,GAAA,CAAK,CAAE,cAAA,CAAgB,YAAa,CAAC,CAAA,CACnDA,EAAI,GAAA,CAAI,WAAW,EACrB,CAOA,eAAsBa,CAAAA,CACpBC,CAAAA,CAA4B,EAAC,CACZ,CACjB,IAAMC,CAAAA,CAAOD,CAAAA,CAAQ,IAAA,EAAQ,GAAA,CACvBF,EAAOE,CAAAA,CAAQ,IAAA,EAAQ,WAAA,CACvBZ,CAAAA,CAASY,EAAQ,MAAA,EAAU,OAAA,CAE3Bb,CAAAA,CAAqB,IAAI,IAEzBe,CAAAA,CAAaC,YAAAA,CAAa,MAAOV,CAAAA,CAAKP,IAAQ,CAClD,GAAI,CACF,MAAMW,EAAaJ,CAAAA,CAAKP,CAAAA,CAAKC,CAAAA,CAAUC,CAAAA,CAAQU,CAAI,EACrD,CAAA,MAASM,CAAAA,CAAK,CACZhB,EAAO,KAAA,CAAM,gCAAA,CAAkCgB,CAAG,CAAA,CAC7ClB,CAAAA,CAAI,aACPA,CAAAA,CAAI,SAAA,CAAU,GAAA,CAAK,CAAE,eAAgB,YAAa,CAAC,CAAA,CAErDA,CAAAA,CAAI,IAAI,uBAAuB,EACjC,CACF,CAAC,EAED,OAAA,MAAM,IAAI,QAAemB,CAAAA,EAAY,CACnCH,EAAW,MAAA,CAAOD,CAAAA,CAAMH,CAAAA,CAAM,IAAM,CAClCV,CAAAA,CAAO,GAAA,CACL,CAAA,+CAAA,EAAkDU,CAAI,IAAIG,CAAI,CAAA,EAAGlB,CAAQ,CAAA,CAC3E,EACAsB,CAAAA,GACF,CAAC,EACH,CAAC,EAEMH,CACT","file":"index.js","sourcesContent":["/**\n * Build a Model Context Protocol server that exposes Directive to MCP\n * clients. Transport-agnostic — call `createDirectiveServer()` to get a\n * configured `McpServer`, then connect it to a stdio transport (local\n * clients) or an SSE transport (hosted at `mcp.directive.run`).\n *\n * This is the SERVER side of the protocol. For the CLIENT side —\n * Directive AI agents calling out to external MCP servers — see\n * `@directive-run/ai/mcp` (`createMCPAdapter`).\n *\n * Current tool surface (knowledge + skill bundles):\n *\n * - `list_knowledge` — every knowledge file name (core + AI + skeleton).\n * - `get_knowledge` — read one knowledge file by name.\n * - `list_examples` — every example file name.\n * - `get_example` — read one example by name.\n * - `search_knowledge` — case-insensitive substring search across all\n * knowledge files, returning the file name + matching lines.\n * - `list_skills` — every Claude Code skill bundled in the plugin.\n * - `get_skill` — read one skill's `SKILL.md` plus its supporting\n * files as a single concatenated document.\n *\n * The package is the umbrella for \"Directive as an MCP server\" — future\n * additions can expose runtime introspection, system state, CLI\n * commands, and debug snapshots through the same binary.\n */\n\nimport { getAllSkills, getSkill } from \"@directive-run/claude-plugin\";\nimport {\n getAllExamples,\n getAllKnowledge,\n getExample,\n getKnowledge,\n} from \"@directive-run/knowledge\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\n\nconst PKG_VERSION = \"0.1.0\";\n\nconst MAX_SEARCH_RESULTS = 50;\nconst MAX_LINE_PREVIEW = 200;\n\nfunction formatLinePreview(line: string): string {\n return line.length > MAX_LINE_PREVIEW\n ? `${line.slice(0, MAX_LINE_PREVIEW)}…`\n : line;\n}\n\nfunction collectSearchHits(\n query: string,\n knowledge: ReadonlyMap<string, string>,\n): string[] {\n const lowered = query.toLowerCase();\n const hits: string[] = [];\n\n for (const [name, content] of knowledge) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n if (!line.toLowerCase().includes(lowered)) {\n continue;\n }\n hits.push(`${name}.md:${i + 1}: ${formatLinePreview(line)}`);\n if (hits.length >= MAX_SEARCH_RESULTS) {\n return hits;\n }\n }\n }\n\n return hits;\n}\n\n/**\n * Build the MCP server with every Directive tool registered.\n *\n * The returned server has no transport attached — connect it to\n * `StdioServerTransport` for local clients or an SSE transport for\n * hosted deployments.\n */\nexport function createDirectiveServer(): McpServer {\n const server = new McpServer({\n name: \"directive\",\n version: PKG_VERSION,\n });\n\n server.registerTool(\n \"list_knowledge\",\n {\n title: \"List Directive knowledge files\",\n description:\n \"List every knowledge file shipped in @directive-run/knowledge. Returns the file names (without .md) that can be passed to get_knowledge. Covers core docs (engine, facts, constraints, resolvers, derivations, effects, plugins, modules, systems, testing) and AI docs (orchestrator, agents, adapters, guardrails, memory, MCP, RAG, security, evals, budget, multi-agent).\",\n inputSchema: {},\n },\n async () => {\n const knowledge = getAllKnowledge();\n const names = Array.from(knowledge.keys()).sort();\n\n return {\n content: [\n {\n type: \"text\",\n text: `${names.length} knowledge files:\\n${names.join(\"\\n\")}`,\n },\n ],\n };\n },\n );\n\n server.registerTool(\n \"get_knowledge\",\n {\n title: \"Get a Directive knowledge file\",\n description:\n \"Fetch the full Markdown contents of one Directive knowledge file by name. Use list_knowledge first to discover available names. Names match the file stem (e.g. 'constraints', 'ai-orchestrator', 'api-skeleton').\",\n inputSchema: {\n name: z\n .string()\n .min(1)\n .describe(\n \"The knowledge file name (no .md suffix). Example: 'constraints', 'ai-orchestrator', 'api-skeleton'.\",\n ),\n },\n },\n async ({ name }) => {\n const content = getKnowledge(name);\n if (!content) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `Knowledge file not found: '${name}'. Call list_knowledge to see available names.`,\n },\n ],\n };\n }\n\n return {\n content: [{ type: \"text\", text: content }],\n };\n },\n );\n\n server.registerTool(\n \"list_examples\",\n {\n title: \"List Directive code examples\",\n description:\n \"List every code example shipped in @directive-run/knowledge. Examples are minimal, working TypeScript files demonstrating one concept each. Pass the returned names to get_example.\",\n inputSchema: {},\n },\n async () => {\n const examples = getAllExamples();\n const names = Array.from(examples.keys()).sort();\n\n return {\n content: [\n {\n type: \"text\",\n text: `${names.length} examples:\\n${names.join(\"\\n\")}`,\n },\n ],\n };\n },\n );\n\n server.registerTool(\n \"get_example\",\n {\n title: \"Get a Directive code example\",\n description:\n \"Fetch the source of one Directive code example by name. Use list_examples first to discover available names. Returns raw TypeScript.\",\n inputSchema: {\n name: z\n .string()\n .min(1)\n .describe(\n \"The example file name (no .ts suffix). Example: 'basic-module', 'ai-orchestrator'.\",\n ),\n },\n },\n async ({ name }) => {\n const content = getExample(name);\n if (!content) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `Example not found: '${name}'. Call list_examples to see available names.`,\n },\n ],\n };\n }\n\n return {\n content: [\n { type: \"text\", text: `\\`\\`\\`typescript\\n${content}\\n\\`\\`\\`` },\n ],\n };\n },\n );\n\n server.registerTool(\n \"search_knowledge\",\n {\n title: \"Search Directive knowledge files\",\n description:\n \"Case-insensitive substring search across every knowledge file. Returns up to 50 matching lines with the file name and line context. Useful for discovering which knowledge file covers a topic before calling get_knowledge for the full document.\",\n inputSchema: {\n query: z\n .string()\n .min(1)\n .describe(\n \"The search string. Matched case-insensitively against every line of every knowledge file.\",\n ),\n },\n },\n async ({ query }) => {\n const hits = collectSearchHits(query, getAllKnowledge());\n\n if (hits.length === 0) {\n return {\n content: [{ type: \"text\", text: `No matches for '${query}'.` }],\n };\n }\n\n const header =\n hits.length === MAX_SEARCH_RESULTS\n ? `${hits.length}+ matches (truncated):`\n : `${hits.length} matches:`;\n\n return {\n content: [{ type: \"text\", text: `${header}\\n${hits.join(\"\\n\")}` }],\n };\n },\n );\n\n server.registerTool(\n \"list_skills\",\n {\n title: \"List Directive Claude Code skills\",\n description:\n \"List every skill bundled in @directive-run/claude-plugin. Each skill is a gerund-named bundle of one SKILL.md plus supporting knowledge files. Pass a returned name to get_skill.\",\n inputSchema: {},\n },\n async () => {\n const skills = getAllSkills();\n const names = Array.from(skills.keys()).sort();\n\n return {\n content: [\n {\n type: \"text\",\n text: `${names.length} skills:\\n${names.join(\"\\n\")}`,\n },\n ],\n };\n },\n );\n\n server.registerTool(\n \"get_skill\",\n {\n title: \"Get a Directive Claude Code skill\",\n description:\n \"Fetch one skill bundle: the SKILL.md manifest plus every supporting knowledge file concatenated into a single document. Use list_skills to discover names.\",\n inputSchema: {\n name: z\n .string()\n .min(1)\n .describe(\n \"The skill name (e.g. 'building-ai-orchestrators', 'writing-directive-constraints').\",\n ),\n },\n },\n async ({ name }) => {\n const skill = getSkill(name);\n if (!skill) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `Skill not found: '${name}'. Call list_skills to see available names.`,\n },\n ],\n };\n }\n\n const parts = [`# Skill: ${skill.name}\\n\\n${skill.manifest}`];\n for (const [fileName, content] of skill.files) {\n parts.push(`---\\n\\n## ${fileName}.md\\n\\n${content}`);\n }\n\n return {\n content: [{ type: \"text\", text: parts.join(\"\\n\\n\") }],\n };\n },\n );\n\n return server;\n}\n","/**\n * SSE (Server-Sent Events) transport for the Directive knowledge MCP\n * server. Hosts the server over HTTP so remote MCP clients\n * (production AI agents, the docs site, third-party tools) can query\n * it at retrieval time — borrowed pattern from zustand's\n * `docs.pmnd.rs/api/sse`.\n *\n * One SSE connection per client. Each connection gets its own\n * MCP server instance so per-session state (subscriptions, list\n * cursors) stays isolated.\n *\n * Endpoints:\n *\n * - `GET /sse` — establish the SSE stream. Server sends a session\n * ID and the client connects `/messages?sessionId=…` for the\n * POST half of the channel.\n * - `POST /messages?sessionId=…` — client→server JSON-RPC messages.\n * - `GET /healthz` — liveness probe returning 200 + `ok`.\n */\n\nimport { type IncomingMessage, type Server, createServer } from \"node:http\";\nimport { SSEServerTransport } from \"@modelcontextprotocol/sdk/server/sse.js\";\nimport { createDirectiveServer } from \"./server.js\";\n\nconst MESSAGES_PATH = \"/messages\";\nconst SSE_PATH = \"/sse\";\nconst HEALTHZ_PATH = \"/healthz\";\n\nexport interface SseServerOptions {\n /** Port to listen on. Default: 3000. */\n port?: number;\n /** Host to bind to. Default: `127.0.0.1`. Use `0.0.0.0` for public deployments. */\n host?: string;\n /** Optional logger. Default: `console`. */\n logger?: Pick<Console, \"log\" | \"warn\" | \"error\">;\n}\n\ninterface ActiveSession {\n transport: SSEServerTransport;\n}\n\ntype Sessions = Map<string, ActiveSession>;\ntype Logger = Pick<Console, \"log\" | \"warn\" | \"error\">;\n\nasync function handleSseConnect(\n res: import(\"node:http\").ServerResponse,\n sessions: Sessions,\n logger: Logger,\n): Promise<void> {\n const transport = new SSEServerTransport(MESSAGES_PATH, res);\n const server = createDirectiveServer();\n sessions.set(transport.sessionId, { transport });\n\n const cleanup = () => {\n sessions.delete(transport.sessionId);\n };\n res.on(\"close\", cleanup);\n transport.onclose = cleanup;\n\n await server.connect(transport);\n logger.log(`[directive-mcp] sse session opened: ${transport.sessionId}`);\n}\n\nasync function handlePostMessage(\n req: IncomingMessage,\n res: import(\"node:http\").ServerResponse,\n url: URL,\n sessions: Sessions,\n): Promise<void> {\n const sessionId = url.searchParams.get(\"sessionId\");\n if (!sessionId) {\n res.writeHead(400, { \"Content-Type\": \"text/plain\" });\n res.end(\"missing sessionId query parameter\");\n return;\n }\n\n const session = sessions.get(sessionId);\n if (!session) {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(`unknown session: ${sessionId}`);\n return;\n }\n\n await session.transport.handlePostMessage(req, res);\n}\n\nasync function routeRequest(\n req: IncomingMessage,\n res: import(\"node:http\").ServerResponse,\n sessions: Sessions,\n logger: Logger,\n host: string,\n): Promise<void> {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? host}`);\n\n if (req.method === \"GET\" && url.pathname === HEALTHZ_PATH) {\n res.writeHead(200, { \"Content-Type\": \"text/plain\" });\n res.end(\"ok\");\n return;\n }\n\n if (req.method === \"GET\" && url.pathname === SSE_PATH) {\n await handleSseConnect(res, sessions, logger);\n return;\n }\n\n if (req.method === \"POST\" && url.pathname === MESSAGES_PATH) {\n await handlePostMessage(req, res, url, sessions);\n return;\n }\n\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"not found\");\n}\n\n/**\n * Start an HTTP server that hosts the Directive knowledge MCP server\n * over SSE. Returns the underlying Node `http.Server` so callers can\n * `close()` it on shutdown.\n */\nexport async function startSseServer(\n options: SseServerOptions = {},\n): Promise<Server> {\n const port = options.port ?? 3000;\n const host = options.host ?? \"127.0.0.1\";\n const logger = options.logger ?? console;\n\n const sessions: Sessions = new Map();\n\n const httpServer = createServer(async (req, res) => {\n try {\n await routeRequest(req, res, sessions, logger, host);\n } catch (err) {\n logger.error(\"[directive-mcp] request error:\", err);\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"text/plain\" });\n }\n res.end(\"internal server error\");\n }\n });\n\n await new Promise<void>((resolve) => {\n httpServer.listen(port, host, () => {\n logger.log(\n `[directive-mcp] sse server listening at http://${host}:${port}${SSE_PATH}`,\n );\n resolve();\n });\n });\n\n return httpServer;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@directive-run/mcp",
3
+ "version": "0.1.0",
4
+ "description": "Model Context Protocol server that exposes Directive to AI clients — knowledge files, code examples, and Claude Code skill bundles today, with room to grow into runtime introspection and tooling. stdio for local clients (Claude Desktop, Cursor, MCP Inspector), SSE for hosted deployments at mcp.directive.run.",
5
+ "license": "(MIT OR Apache-2.0)",
6
+ "author": "Jason Comes",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/directive-run/directive",
10
+ "directory": "packages/mcp"
11
+ },
12
+ "homepage": "https://directive.run/docs/ide-integration",
13
+ "bugs": {
14
+ "url": "https://github.com/directive-run/directive/issues"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public",
18
+ "registry": "https://registry.npmjs.org"
19
+ },
20
+ "engines": {
21
+ "node": ">=18"
22
+ },
23
+ "keywords": [
24
+ "directive",
25
+ "mcp",
26
+ "model-context-protocol",
27
+ "knowledge",
28
+ "ai-rules",
29
+ "sse",
30
+ "stdio"
31
+ ],
32
+ "type": "module",
33
+ "bin": {
34
+ "directive-mcp": "./dist/cli.js"
35
+ },
36
+ "main": "./dist/index.cjs",
37
+ "module": "./dist/index.js",
38
+ "types": "./dist/index.d.ts",
39
+ "exports": {
40
+ ".": {
41
+ "types": "./dist/index.d.ts",
42
+ "require": "./dist/index.cjs",
43
+ "import": "./dist/index.js"
44
+ }
45
+ },
46
+ "files": [
47
+ "dist"
48
+ ],
49
+ "dependencies": {
50
+ "@modelcontextprotocol/sdk": "^1.0.0",
51
+ "zod": "^3.23.0",
52
+ "@directive-run/knowledge": "1.16.0",
53
+ "@directive-run/claude-plugin": "1.16.0"
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^25.2.0",
57
+ "tsup": "^8.3.5",
58
+ "tsx": "^4.19.2",
59
+ "typescript": "^5.7.2",
60
+ "vitest": "^3.0.0"
61
+ },
62
+ "scripts": {
63
+ "build": "tsup",
64
+ "test": "vitest run",
65
+ "typecheck": "tsc --noEmit",
66
+ "clean": "rm -rf dist"
67
+ }
68
+ }