@systemdox/mcp-server 0.1.45 → 0.1.47

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.
@@ -77,18 +77,9 @@ async function walk(root, { skipHidden }, dir = root, out = { files: [], links:
77
77
  out.files.sort();
78
78
  return out;
79
79
  }
80
- async function readSkillDirectory(directory) {
81
- const root = resolve(directory);
82
- if (!existsSync(join(root, ENTRY_FILE))) {
83
- throw new Error(`${root} has no ${ENTRY_FILE}: a skill folder holds SKILL.md at its root`);
84
- }
85
- const walked = await walk(root, { skipHidden: true });
86
- if (walked.links.length) {
87
- throw new Error(`${walked.links.join(", ")}: links are not followed; copy the file into the skill folder`);
88
- }
89
- const paths = walked.files;
80
+ async function readTextFiles(root, paths, what) {
90
81
  if (paths.length > MAX_FILES) {
91
- throw new Error(`${root} holds ${paths.length} files: a skill holds at most ${MAX_FILES}`);
82
+ throw new Error(`${root} holds ${paths.length} files: ${what} holds at most ${MAX_FILES}`);
92
83
  }
93
84
  const decoder = new TextDecoder("utf-8", { fatal: true });
94
85
  const files = [];
@@ -98,7 +89,7 @@ async function readSkillDirectory(directory) {
98
89
  throw new Error(`${path}: file names may use letters, digits, ".", "_" and "-" only`);
99
90
  }
100
91
  const raw = await readFile(join(root, ...path.split("/")));
101
- if (raw.includes(0)) throw new Error(`${path} is binary: a skill holds text files only`);
92
+ if (raw.includes(0)) throw new Error(`${path} is binary: ${what} holds text files only`);
102
93
  if (raw.length > MAX_FILE_BYTES) {
103
94
  throw new Error(`${path} is ${raw.length} bytes: a file holds at most ${MAX_FILE_BYTES}`);
104
95
  }
@@ -114,6 +105,18 @@ async function readSkillDirectory(directory) {
114
105
  }
115
106
  files.push({ path, content });
116
107
  }
108
+ return files;
109
+ }
110
+ async function readSkillDirectory(directory) {
111
+ const root = resolve(directory);
112
+ if (!existsSync(join(root, ENTRY_FILE))) {
113
+ throw new Error(`${root} has no ${ENTRY_FILE}: a skill folder holds SKILL.md at its root`);
114
+ }
115
+ const walked = await walk(root, { skipHidden: true });
116
+ if (walked.links.length) {
117
+ throw new Error(`${walked.links.join(", ")}: links are not followed; copy the file into the skill folder`);
118
+ }
119
+ const files = await readTextFiles(root, walked.files, "a skill");
117
120
  const skillMd = files.find((f) => f.path === ENTRY_FILE);
118
121
  const name = frontmatterName(skillMd.content);
119
122
  if (!name) throw new Error(`${ENTRY_FILE} has no frontmatter name`);
@@ -0,0 +1,37 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { SystemDoxApiClient } from "../lib/api-client.js";
3
+ import { type FileIn, type PartName } from "../lib/team-setup-types.js";
4
+ import { type SyncClient } from "../sync-memory.js";
5
+ /**
6
+ * The team setup over MCP (ADR-217 with Amendments 1 and 2,
7
+ * specs/changes/team-setup/contract.md):
8
+ *
9
+ * - `publish_setup_part` publishes one part of the team setup (hooks, MCP
10
+ * servers, agents, commands, instructions), with publish_skill's reading
11
+ * rules: the local server reads the folder itself, only the paths the part
12
+ * accepts, and refuses a link, a binary file or a file past ADR-216's limits
13
+ * before anything is sent; the hosted server takes `files`.
14
+ * - `resolve_person` reads back a person a memory names by token, for this
15
+ * session only. It writes nothing and keeps nothing.
16
+ * - `sync_memory` (local server only: it needs the disk) runs `sync-memory`
17
+ * with no person at a terminal: it never answers a question about a value
18
+ * and never rewrites a file for one; it names the command for the member.
19
+ *
20
+ * Both act on the key's own workspace, which `GET /team-setup` names.
21
+ */
22
+ type Client = Pick<SystemDoxApiClient, "getOwnTeamSetup" | "publishPartVersion" | "resolvePerson"> & Partial<SyncClient>;
23
+ /**
24
+ * Read the files `part` accepts from `directory`, and nothing else. A link on
25
+ * the way to one of them (the file, its folder, anything inside a folder read
26
+ * whole) stops the read before any file is read. Returns the files and what
27
+ * was left out inside the folders read (hidden files, and files that are not
28
+ * `*.md` where only those are accepted).
29
+ */
30
+ export declare function readPartDirectory(part: PartName, directory: string): Promise<{
31
+ files: FileIn[];
32
+ leftOut: string[];
33
+ }>;
34
+ export declare function registerTeamSetupTools(server: McpServer, apiClient: Client, { localFiles }: {
35
+ localFiles: boolean;
36
+ }): void;
37
+ export {};