@nonbot/mcp 0.0.0-stage → 0.2.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/dist/stdio.js ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ import { mkdir, writeFile, readFile } from 'node:fs/promises';
3
+ import { homedir } from 'node:os';
4
+ import { dirname, join, resolve } from 'node:path';
5
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
6
+ import { resolveApiBase } from './core/config.js';
7
+ import { createNonbotMcpServer } from './core/server.js';
8
+ const BACKUP_DIR = join(homedir(), '.non.bot', 'backups');
9
+ async function tokenFromCliAuth() {
10
+ try {
11
+ const configDir = process.env.NONBOT_CONFIG_DIR || join(homedir(), '.config', 'nonbot');
12
+ const profile = process.env.NONBOT_PROFILE;
13
+ const file = profile && /^[a-zA-Z0-9_-]{1,32}$/.test(profile)
14
+ ? join(configDir, 'profiles', `${profile}.json`)
15
+ : join(configDir, 'auth.json');
16
+ const parsed = JSON.parse(await readFile(file, 'utf8'));
17
+ return typeof parsed?.pat === 'string' && parsed.pat.startsWith('pat_') ? parsed.pat : '';
18
+ }
19
+ catch {
20
+ return '';
21
+ }
22
+ }
23
+ const PORTFOLIO_ID_SHAPE = /^[A-Za-z0-9_-]{1,80}$/;
24
+ async function fileBackup(id, json) {
25
+ if (typeof id !== 'string' || !PORTFOLIO_ID_SHAPE.test(id)) {
26
+ throw new Error(`Refusing to back up portfolio with unsafe id shape: ${JSON.stringify(id)}`);
27
+ }
28
+ await mkdir(BACKUP_DIR, { recursive: true });
29
+ const file = join(BACKUP_DIR, `${id}-${Date.now()}.json`);
30
+ if (dirname(resolve(file)) !== resolve(BACKUP_DIR)) {
31
+ throw new Error('Refusing to write portfolio backup outside the backup directory');
32
+ }
33
+ await writeFile(file, JSON.stringify(json, null, 2), 'utf8');
34
+ }
35
+ const IMAGE_MIME = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', webp: 'image/webp', gif: 'image/gif' };
36
+ async function readLocalImage(path) {
37
+ const ext = path.toLowerCase().split('.').pop() ?? '';
38
+ const mime = IMAGE_MIME[ext];
39
+ if (!mime)
40
+ throw new Error('file_path must be a .png, .jpg, .jpeg, .webp or .gif image');
41
+ const buf = await readFile(resolve(path));
42
+ if (buf.length > 2_200_000)
43
+ throw new Error(`The image is ${(buf.length / 1e6).toFixed(1)} MB; the limit is 2.2 MB — downscale it first`);
44
+ const magic = buf.subarray(0, 12);
45
+ const ok = mime === 'image/png' ? magic[0] === 0x89 && magic[1] === 0x50
46
+ : mime === 'image/jpeg' ? magic[0] === 0xff && magic[1] === 0xd8
47
+ : mime === 'image/gif' ? magic.toString('latin1', 0, 3) === 'GIF'
48
+ : magic.toString('latin1', 0, 4) === 'RIFF' && magic.toString('latin1', 8, 12) === 'WEBP';
49
+ if (!ok)
50
+ throw new Error('file_path is not the image its extension says it is');
51
+ return `data:${mime};base64,${buf.toString('base64')}`;
52
+ }
53
+ async function main() {
54
+ const inRun = typeof process.env.NONBOT_RUN_ID === 'string' && process.env.NONBOT_RUN_ID.length > 0;
55
+ const token = inRun
56
+ ? (process.env.NONBOT_PAT || '')
57
+ : (process.env.NON_BOT_API_TOKEN || (await tokenFromCliAuth()));
58
+ const ctx = {
59
+ apiBase: resolveApiBase(process.env.NON_BOT_API_BASE),
60
+ getToken: () => token,
61
+ readonly: process.env.NON_BOT_MCP_READONLY === '1',
62
+ backup: fileBackup,
63
+ readLocalImage,
64
+ };
65
+ const server = createNonbotMcpServer(ctx);
66
+ const transport = new StdioServerTransport();
67
+ await server.connect(transport);
68
+ }
69
+ main().catch((err) => {
70
+ process.stderr.write(`nonbot-mcp failed to start: ${err instanceof Error ? err.message : String(err)}\n`);
71
+ process.exit(1);
72
+ });
package/package.json CHANGED
@@ -1,6 +1,67 @@
1
1
  {
2
2
  "name": "@nonbot/mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "type": "module",
5
+ "description": "The non.bot MCP server — one server for your portfolios, decision graph, and Runs. Runs over stdio (Claude Code, Cursor, VS Code) with a pat_ token. Built on MCP protocol 2025-11-25.",
6
+ "keywords": [
7
+ "mcp",
8
+ "model-context-protocol",
9
+ "mcp-server",
10
+ "non.bot",
11
+ "portfolio",
12
+ "product-management",
13
+ "decision-graph",
14
+ "claude",
15
+ "claude-code",
16
+ "cursor",
17
+ "vscode"
18
+ ],
19
+ "license": "UNLICENSED",
20
+ "private": false,
21
+ "homepage": "https://non.bot/wiki/integrations",
22
+ "bugs": {
23
+ "url": "https://non.bot/contact"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/conrad-pm/nonbot.git",
28
+ "directory": "nonbot-mcp"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "bin": {
34
+ "nonbot-mcp": "dist/stdio.js"
35
+ },
36
+ "main": "dist/core/server.js",
37
+ "types": "dist/core/server.d.ts",
38
+ "files": [
39
+ "dist",
40
+ "README.md",
41
+ "LICENSE"
42
+ ],
43
+ "engines": {
44
+ "node": ">=18"
45
+ },
46
+ "scripts": {
47
+ "mcp": "tsx src/stdio.ts",
48
+ "dev": "tsx src/stdio.ts",
49
+ "start": "node dist/stdio.js",
50
+ "build": "tsc -p tsconfig.build.json",
51
+ "typecheck": "tsc --noEmit",
52
+ "test": "vitest run",
53
+ "gen:tools": "node scripts/gen-tool-table.mjs",
54
+ "prepublishOnly": "npm run build"
55
+ },
56
+ "dependencies": {
57
+ "@cfworker/json-schema": "^4.1.1",
58
+ "@modelcontextprotocol/sdk": "^1.29.0",
59
+ "zod": "^3.25.0"
60
+ },
61
+ "devDependencies": {
62
+ "@types/node": "^22.19.19",
63
+ "tsx": "^4.22.4",
64
+ "typescript": "^5.7.0",
65
+ "vitest": "^4.1.8"
66
+ }
67
+ }