mcpill 1.3.0 → 1.6.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.
@@ -1,100 +0,0 @@
1
- import { writeFileSync, mkdirSync } from 'fs';
2
- import { join } from 'path';
3
- import type { ServerDoc, ResourceDoc } from './types.js';
4
-
5
- function serializeResources(resources: ResourceDoc[]): string {
6
- return resources
7
- .map((r) => {
8
- let fm = `uri: ${r.uri}`;
9
- if (r.name) fm += `\nname: ${r.name}`;
10
- if (r.mimeType) fm += `\nmimeType: ${r.mimeType}`;
11
- return `${fm}\n---\n${r.content}`;
12
- })
13
- .join('\n---\n');
14
- }
15
-
16
- export function serializeServerDoc(doc: ServerDoc): string {
17
- const configSection =
18
- `## Config\n\`\`\`json\n${JSON.stringify(doc.config, null, 2)}\n\`\`\``;
19
-
20
- const toolParts = doc.tools.map((tool) => {
21
- let part = `### ${tool.name}\n**Description:** ${tool.description}\n\n`;
22
- part += `\`\`\`json\n${JSON.stringify(tool.schema, null, 2)}\n\`\`\``;
23
- if (tool.handler !== undefined) {
24
- part +=
25
- `\n\n\`\`\`js\n// @handler:${tool.name}\n${tool.handler}\n// @end-handler:${tool.name}\n\`\`\``;
26
- }
27
- return part;
28
- });
29
- const toolsSection =
30
- doc.tools.length > 0
31
- ? `## Tools\n\n${toolParts.join('\n\n')}`
32
- : `## Tools`;
33
-
34
- const promptsSection =
35
- `## Prompts\n\`\`\`json\n${JSON.stringify(doc.prompts, null, 2)}\n\`\`\``;
36
-
37
- const resourcesSection =
38
- doc.resources.length > 0
39
- ? `## Resources\n${serializeResources(doc.resources)}`
40
- : `## Resources`;
41
-
42
- return [configSection, toolsSection, promptsSection, resourcesSection].join('\n\n') + '\n';
43
- }
44
-
45
- export function serializeServerDir(doc: ServerDoc, dir: string): void {
46
- mkdirSync(join(dir, 'server', 'tools'), { recursive: true });
47
- mkdirSync(join(dir, 'server', 'prompts'), { recursive: true });
48
-
49
- let serverMd = '## Config\n';
50
- serverMd += `name: ${doc.config.name}\n`;
51
- serverMd += `transport: ${doc.config.transport}\n`;
52
- if (doc.config.port !== undefined) serverMd += `port: ${doc.config.port}\n`;
53
-
54
- if (doc.resources.length > 0) {
55
- serverMd += `\n## Resources\n${serializeResources(doc.resources)}\n`;
56
- } else {
57
- serverMd += '\n## Resources\n';
58
- }
59
-
60
- writeFileSync(join(dir, 'server.md'), serverMd);
61
- console.log('✓ server.md updated');
62
-
63
- for (const tool of doc.tools) {
64
- let md = `# ${tool.name}\n\n${tool.description}\n\n## Parameters\n\n`;
65
- for (const [pName, pDef] of Object.entries(tool.schema)) {
66
- md += `- ${pName} (${pDef.type}): ${pDef.description ?? pName}\n`;
67
- }
68
- if (tool.handler !== undefined) {
69
- const cleanHandler = tool.handler
70
- .split('\n')
71
- .filter((l) => !l.trim().startsWith('// @handler:') && !l.trim().startsWith('// @end-handler:'))
72
- .join('\n')
73
- .trim();
74
- md += `\n## Handler\n\n\`\`\`js\n${cleanHandler}\n\`\`\`\n`;
75
- }
76
- writeFileSync(join(dir, 'server', 'tools', `${tool.name}.md`), md);
77
- }
78
-
79
- for (const prompt of doc.prompts) {
80
- let md = `# ${prompt.name}\n\n`;
81
- if (prompt.description) md += `${prompt.description}\n\n`;
82
- if (Object.keys(prompt.args).length > 0) {
83
- md += '## Args\n\n';
84
- for (const [argName, argDef] of Object.entries(prompt.args)) {
85
- md += `- ${argName} (${argDef.type}): ${argName}\n`;
86
- }
87
- md += '\n';
88
- }
89
- if (prompt.messages.length > 0) {
90
- md += '## Message\n\n';
91
- for (const msg of prompt.messages) {
92
- md += `> ${msg.role}: ${msg.content}\n`;
93
- }
94
- }
95
- writeFileSync(join(dir, 'server', 'prompts', `${prompt.name}.md`), md);
96
- }
97
-
98
- console.log(`✓ tools/ updated (${doc.tools.length} files)`);
99
- console.log(`✓ prompts/ updated (${doc.prompts.length} files)`);
100
- }
@@ -1,27 +0,0 @@
1
- export type ToolDoc = {
2
- name: string;
3
- description: string;
4
- schema: Record<string, { type: 'string' | 'number' | 'boolean'; description?: string }>;
5
- handler?: string;
6
- };
7
-
8
- export type PromptDoc = {
9
- name: string;
10
- description?: string;
11
- args: Record<string, { type: string }>;
12
- messages: Array<{ role: string; content: string }>;
13
- };
14
-
15
- export type ResourceDoc = {
16
- uri: string;
17
- name?: string;
18
- mimeType?: string;
19
- content: string;
20
- };
21
-
22
- export type ServerDoc = {
23
- config: { name: string; transport: 'stdio' | 'http'; port?: number };
24
- tools: ToolDoc[];
25
- prompts: PromptDoc[];
26
- resources: ResourceDoc[];
27
- };
@@ -1,25 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
-
4
- export type CliConfig = {
5
- name: string;
6
- transport: "stdio" | "http";
7
- port: number;
8
- };
9
-
10
- const defaults: CliConfig = {
11
- name: "mcpill-server",
12
- transport: "stdio",
13
- port: 3333,
14
- };
15
-
16
- export async function loadConfig(mcpillDir: string): Promise<CliConfig> {
17
- const configPath = path.join(mcpillDir, "mcpill.config.json");
18
- if (!fs.existsSync(configPath)) {
19
- return { ...defaults };
20
- }
21
- const raw = JSON.parse(
22
- fs.readFileSync(configPath, "utf-8")
23
- ) as Partial<CliConfig>;
24
- return { ...defaults, ...raw };
25
- }
@@ -1,60 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { z } from "mcpill-runtime";
4
-
5
- type ZodRawShape = Record<string, z.ZodTypeAny>;
6
-
7
- export type RawPromptDef = {
8
- name: string;
9
- description?: string;
10
- argsShape: ZodRawShape;
11
- messages: Array<{ role: string; content: string }>;
12
- };
13
-
14
- const typeMap: Record<string, z.ZodTypeAny> = {
15
- string: z.string(),
16
- number: z.number(),
17
- boolean: z.boolean(),
18
- };
19
-
20
- export async function loadPrompts(mcpillDir: string): Promise<RawPromptDef[]> {
21
- const promptsPath = path.join(mcpillDir, "prompts.json");
22
- let raw: unknown;
23
- try {
24
- const content = fs.readFileSync(promptsPath, "utf-8");
25
- raw = JSON.parse(content);
26
- } catch (err) {
27
- const msg = err instanceof Error ? err.message : String(err);
28
- throw new Error(`prompts.json is not valid JSON: ${msg}`);
29
- }
30
-
31
- if (!Array.isArray(raw)) {
32
- throw new Error("prompts.json is not valid JSON: must be an array");
33
- }
34
-
35
- const prompts: RawPromptDef[] = [];
36
-
37
- for (const entry of raw as Array<Record<string, unknown>>) {
38
- const argsShape: ZodRawShape = {};
39
- const args = (entry["args"] ?? {}) as Record<string, { type: string }>;
40
-
41
- for (const [argName, argDef] of Object.entries(args)) {
42
- const typeName = argDef.type;
43
- if (!(typeName in typeMap)) {
44
- throw new Error(
45
- `Prompt '${entry["name"]}': arg '${argName}' uses unsupported type '${typeName}'. Supported: string, number, boolean`
46
- );
47
- }
48
- argsShape[argName] = typeMap[typeName]!;
49
- }
50
-
51
- prompts.push({
52
- name: entry["name"] as string,
53
- description: entry["description"] as string | undefined,
54
- argsShape,
55
- messages: entry["messages"] as Array<{ role: string; content: string }>,
56
- });
57
- }
58
-
59
- return prompts;
60
- }
@@ -1,54 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
-
4
- export type RawResourceDef = {
5
- uri: string;
6
- name?: string;
7
- mimeType?: string;
8
- content: string;
9
- };
10
-
11
- function parseFrontmatter(block: string): Record<string, string> {
12
- const result: Record<string, string> = {};
13
- for (const line of block.split("\n")) {
14
- const colonIdx = line.indexOf(":");
15
- if (colonIdx === -1) continue;
16
- const key = line.slice(0, colonIdx).trim();
17
- const value = line.slice(colonIdx + 1).trim();
18
- if (key) result[key] = value;
19
- }
20
- return result;
21
- }
22
-
23
- export async function loadResources(mcpillDir: string): Promise<RawResourceDef[]> {
24
- const resourcesPath = path.join(mcpillDir, "resources.md");
25
- const fileContent = fs.readFileSync(resourcesPath, "utf-8");
26
- const blocks = fileContent.split("\n---\n");
27
-
28
- const resources: RawResourceDef[] = [];
29
-
30
- // blocks alternate: [frontmatter, body, frontmatter, body, ...]
31
- // even indices (0-indexed) = frontmatter = block N (1-indexed odd)
32
- for (let i = 0; i < blocks.length; i += 2) {
33
- const frontmatterBlock = blocks[i] ?? "";
34
- if (frontmatterBlock.trim() === "") continue;
35
-
36
- const bodyBlock = blocks[i + 1] ?? "";
37
- const frontmatter = parseFrontmatter(frontmatterBlock);
38
-
39
- if (!frontmatter["uri"]) {
40
- throw new Error(
41
- `resources.md block ${i + 1} is missing required frontmatter field: uri`
42
- );
43
- }
44
-
45
- resources.push({
46
- uri: frontmatter["uri"],
47
- name: frontmatter["name"],
48
- mimeType: frontmatter["mimeType"],
49
- content: bodyBlock.trim(),
50
- });
51
- }
52
-
53
- return resources;
54
- }
@@ -1,68 +0,0 @@
1
- import { pathToFileURL } from "url";
2
- import path from "path";
3
-
4
- export type RawToolDef = {
5
- name: string;
6
- description: string;
7
- schema: Record<string, unknown>;
8
- // eslint-disable-next-line @typescript-eslint/ban-types
9
- handler: Function;
10
- };
11
-
12
- export async function loadTools(mcpillDir: string): Promise<RawToolDef[]> {
13
- const toolsPath = path.join(mcpillDir, "tools.js");
14
- let mod: unknown;
15
- try {
16
- mod = await import(pathToFileURL(toolsPath).href);
17
- } catch (err) {
18
- const msg = err instanceof Error ? err.message : String(err);
19
- if (msg.includes("mcpill-runtime")) {
20
- throw new Error(
21
- "tools.js failed to load: mcpill-runtime not found.\nRun: npm install mcpill-runtime"
22
- );
23
- }
24
- throw new Error(`tools.js failed to load: ${msg}`);
25
- }
26
-
27
- const tools = (mod as { default?: unknown }).default;
28
- if (!Array.isArray(tools)) {
29
- throw new Error("tools.js failed to load: default export must be an array");
30
- }
31
-
32
- for (let i = 0; i < tools.length; i++) {
33
- const tool = tools[i] as Record<string, unknown>;
34
-
35
- for (const field of ["name", "description", "schema", "handler"] as const) {
36
- if (tool[field] === undefined || tool[field] === null) {
37
- throw new Error(`Tool at index ${i} is missing required field: ${field}`);
38
- }
39
- }
40
-
41
- if (typeof tool["name"] !== "string") {
42
- throw new Error(`Tool at index ${i} is missing required field: name`);
43
- }
44
- if (typeof tool["description"] !== "string") {
45
- throw new Error(`Tool at index ${i} is missing required field: description`);
46
- }
47
- if (
48
- typeof tool["schema"] !== "object" ||
49
- Array.isArray(tool["schema"]) ||
50
- tool["schema"] === null
51
- ) {
52
- throw new Error(`Tool at index ${i} is missing required field: schema`);
53
- }
54
- const name = tool["name"] as string;
55
- for (const [key, value] of Object.entries(tool["schema"] as Record<string, unknown>)) {
56
- if (value == null || typeof value !== "object" || !("_def" in (value as object))) {
57
- throw new Error(
58
- `Tool '${name}': schema field '${key}' must be a Zod type (e.g. z.string()). Got a plain value instead.`
59
- );
60
- }
61
- }
62
- if (typeof tool["handler"] !== "function") {
63
- throw new Error(`Tool '${tool["name"]}': handler must be a function`);
64
- }
65
- }
66
-
67
- return tools as RawToolDef[];
68
- }
@@ -1,11 +0,0 @@
1
- # greeting
2
-
3
- Generate a greeting
4
-
5
- ## Args
6
-
7
- - name (string): The name to greet
8
-
9
- ## Message
10
-
11
- > user: Say hello to {{name}}
@@ -1,9 +0,0 @@
1
- ## Config
2
- name: my-server
3
- transport: stdio
4
-
5
- ## Resources
6
- uri: info://status
7
- name: Status
8
- ---
9
- The server is running.
@@ -1,15 +0,0 @@
1
- # echo
2
-
3
- Echo a message back
4
-
5
- ## Parameters
6
-
7
- - message (string): The message to echo
8
-
9
- ## Handler
10
-
11
- ```js
12
- async ({ message }) => {
13
- return { content: [{ type: "text", text: message }] };
14
- }
15
- ```
package/tsconfig.json DELETED
@@ -1,10 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "strict": true,
7
- "outDir": "dist"
8
- },
9
- "include": ["src"]
10
- }
package/tsup.config.ts DELETED
@@ -1,13 +0,0 @@
1
- import { defineConfig } from "tsup";
2
-
3
- export default defineConfig({
4
- entry: ["src/cli.ts"],
5
- format: ["esm"],
6
- target: "node18",
7
- platform: "node",
8
- shims: true,
9
- banner: {
10
- js: "#!/usr/bin/env node",
11
- },
12
- clean: true,
13
- });
package/vitest.config.ts DELETED
@@ -1,12 +0,0 @@
1
- import { defineConfig } from "vitest/config";
2
-
3
- export default defineConfig({
4
- test: {
5
- globals: true,
6
- environment: "node",
7
- coverage: {
8
- provider: "v8",
9
- reporter: ["text", "lcov"],
10
- },
11
- },
12
- });