@biznetgio/neo-app 0.4.10 → 0.4.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biznetgio/neo-app",
3
- "version": "0.4.10",
3
+ "version": "0.4.12",
4
4
  "description": "NEO App CLI — one-command agent onboarding, MCP server, and project ops for coding agents (Codex, Claude Code).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,7 +15,8 @@
15
15
  "node": ">=18"
16
16
  },
17
17
  "scripts": {
18
- "test": "node --test"
18
+ "test": "node --test",
19
+ "prepublishOnly": "node --test"
19
20
  },
20
21
  "dependencies": {
21
22
  "@modelcontextprotocol/sdk": "^1.0.0"
package/src/index.js CHANGED
@@ -1,19 +1,7 @@
1
1
  // CLI entrypoint: parse args and dispatch. Keep this thin — logic lives in commands/.
2
2
 
3
3
  import { parseArgs } from 'node:util';
4
- import { readFileSync } from 'node:fs';
5
- import { fileURLToPath } from 'node:url';
6
- import { dirname, join } from 'node:path';
7
- import { c, fail } from './util.js';
8
-
9
- function version() {
10
- try {
11
- const p = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
12
- return JSON.parse(readFileSync(p, 'utf8')).version;
13
- } catch {
14
- return 'dev';
15
- }
16
- }
4
+ import { c, fail, pkgVersion as version } from './util.js';
17
5
 
18
6
  const HELP = `${c.bold('neo-app')} — NEO App CLI for coding agents
19
7
 
package/src/mcp/server.js CHANGED
@@ -9,6 +9,7 @@ import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprot
9
9
  import { resolveConfig } from '../config.js';
10
10
  import { getCredential } from '../creds.js';
11
11
  import { NeoApi } from '../api.js';
12
+ import { pkgVersion } from '../util.js';
12
13
  import { TOOLS } from './tools.js';
13
14
 
14
15
  export async function runMcpServer() {
@@ -19,7 +20,9 @@ export async function runMcpServer() {
19
20
  console.error(`[neo-app mcp] no credential for ${cfg.baseUrl} — run 'neo-app login' or set NEO_APP_TOKEN`);
20
21
  }
21
22
 
22
- const server = new Server({ name: 'neo-app', version: '0.3.0' }, { capabilities: { tools: {} } });
23
+ // serverInfo.version MUST equal the packed npm artifact version (handoff 3.1):
24
+ // derive it from package.json, never hard-code it.
25
+ const server = new Server({ name: 'neo-app', version: pkgVersion() }, { capabilities: { tools: {} } });
23
26
 
24
27
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
25
28
  tools: TOOLS.map((t) => ({
package/src/mcp/tools.js CHANGED
@@ -5,6 +5,7 @@
5
5
  import { resolve } from 'node:path';
6
6
  import { statSync } from 'node:fs';
7
7
  import { deploySource } from '../deploySource.js';
8
+ import { pkgVersion } from '../util.js';
8
9
 
9
10
  const str = (description) => ({ type: 'string', description });
10
11
  const int = (description) => ({ type: 'integer', description });
@@ -32,6 +33,59 @@ export const TOOLS = [
32
33
  return api.whoami();
33
34
  },
34
35
  },
36
+ {
37
+ name: 'neo_get_capabilities',
38
+ description:
39
+ 'Discover this MCP server version, the control-plane it targets, and which resource types, lifecycle verbs, source types and environments are actually supported today. Call this FIRST to check compatibility before planning a deployment. The support matrix is honest about current gaps (handoff 3.2).',
40
+ mutating: false,
41
+ schema: { type: 'object', properties: {} },
42
+ async call(api) {
43
+ // Control-plane version (open /version endpoint). Best-effort: a null
44
+ // version just means that probe failed, not that the server is down.
45
+ const controlPlane = { baseUrl: api.baseUrl, version: null, gitCommit: null };
46
+ try {
47
+ const v = (await api.get('/version')).data || {};
48
+ controlPlane.version = v.version ?? null;
49
+ controlPlane.gitCommit = v.gitCommit ?? null;
50
+ } catch { /* leave nulls */ }
51
+
52
+ // Price book is readable by any member; surface whether cost data is
53
+ // available (cost consent / neo_plan_stack will consume it).
54
+ const pricing = { readable: false, version: null };
55
+ try {
56
+ const p = (await api.get('/api/v1/pricing')).data;
57
+ pricing.readable = true;
58
+ pricing.version = (p && !Array.isArray(p) && p.version) || null;
59
+ } catch { /* not readable */ }
60
+
61
+ // Honest support matrix: what THIS build does, not the roadmap. `create`
62
+ // is true, false, or 'via_folder' (created through neo_deploy_folder).
63
+ const resourceTypes = [
64
+ { type: 'web', create: true, deploy: true, scale: true, logs: true, variables: true, delete: false },
65
+ { type: 'template', create: true, deploy: true, scale: false, logs: true, variables: true, delete: false },
66
+ { type: 'static_site', create: false, deploy: true, scale: false, logs: true, variables: true, delete: false, note: 'currently planned/created as web (handoff 4.3)' },
67
+ { type: 'worker', create: false, deploy: true, scale: true, logs: true, variables: true, delete: false, note: 'currently planned/created as web (handoff 4.2)' },
68
+ { type: 'database', create: false, deploy: false, delete: false, note: 'REST exists; no MCP tool yet (handoff 4.4)' },
69
+ { type: 'queue', create: false, deploy: false, delete: false, note: 'REST exists; no MCP tool yet (handoff 4.5)' },
70
+ { type: 'cron', create: false, deploy: false, delete: false, note: 'REST exists; no MCP tool yet (handoff 4.6)' },
71
+ { type: 'object_storage', create: false, deploy: false, delete: false, note: 'REST exists; no MCP tool yet (handoff 4.7)' },
72
+ ];
73
+
74
+ return {
75
+ mcp: { name: 'neo-app', version: pkgVersion() },
76
+ controlPlane,
77
+ resourceTypes,
78
+ // Verbs this MCP build exposes today (no create-DB/queue/cron/bucket, no delete, no costed plan/apply yet).
79
+ lifecycleVerbs: ['list', 'get', 'deploy', 'scale', 'logs', 'get_variables', 'set_variable', 'list_approvals', 'get_approval', 'get_credit', 'list_templates'],
80
+ sourceTypes: ['git', 'image', 'dockerfile', 'local', 'template'],
81
+ environments: ['staging', 'production'],
82
+ pricing,
83
+ costConsent: { planApplyEnforced: false, note: 'neo_plan_stack / Approve & Create not implemented yet (handoff 3.3-3.4)' },
84
+ minClientVersion: pkgVersion(),
85
+ deprecations: [],
86
+ };
87
+ },
88
+ },
35
89
  {
36
90
  name: 'neo_list_projects',
37
91
  description: 'List projects in an organization. Omit orgId to use the first org.',
package/src/util.js CHANGED
@@ -1,9 +1,23 @@
1
1
  // Small shared helpers: colored status lines, a config-file backup, and a PATH check.
2
2
 
3
- import { existsSync, copyFileSync } from 'node:fs';
3
+ import { existsSync, copyFileSync, readFileSync } from 'node:fs';
4
4
  import { execFileSync } from 'node:child_process';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { dirname, join } from 'node:path';
5
7
  import readline from 'node:readline';
6
8
 
9
+ // The installed package version — the single source for `--version`, the MCP
10
+ // handshake (serverInfo.version), and any other place that must report it.
11
+ // Derived from the packed package.json so it always matches the npm artifact.
12
+ export function pkgVersion() {
13
+ try {
14
+ const p = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
15
+ return JSON.parse(readFileSync(p, 'utf8')).version;
16
+ } catch {
17
+ return 'dev';
18
+ }
19
+ }
20
+
7
21
  const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
8
22
  const paint = (code, s) => (useColor ? `\x1b[${code}m${s}\x1b[0m` : s);
9
23