@neus/mcp-server 1.0.0 → 1.0.1

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,103 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Print a compact neus_context view for MCP connectivity checks.
5
- * Usage: MCP_E2E_BASE_URL=http://127.0.0.1:3110 node test-agent-context.js
6
- */
7
-
8
- const BASE_URL = String(process.env.MCP_E2E_BASE_URL || 'https://mcp.neus.network').replace(/\/$/, '');
9
-
10
- function parseSseJson(raw) {
11
- const dataLine = String(raw || '')
12
- .split(/\r?\n/)
13
- .find((line) => line.startsWith('data: '));
14
- if (!dataLine) throw new Error(`Unexpected MCP response: ${raw.slice(0, 2000)}`);
15
- return JSON.parse(dataLine.slice('data: '.length));
16
- }
17
-
18
- async function initMcpSession(baseUrl) {
19
- const headers = {
20
- 'content-type': 'application/json',
21
- 'mcp-protocol-version': '2025-11-25',
22
- accept: 'application/json, text/event-stream'
23
- };
24
- const response = await fetch(`${baseUrl}/mcp`, {
25
- method: 'POST',
26
- headers,
27
- body: JSON.stringify({
28
- jsonrpc: '2.0',
29
- id: 1,
30
- method: 'initialize',
31
- params: {
32
- protocolVersion: '2025-11-25',
33
- capabilities: {},
34
- clientInfo: { name: 'agent-context-probe', version: '1.0.0' }
35
- }
36
- })
37
- });
38
- const raw = await response.text();
39
- const sessionId = response.headers.get('mcp-session-id');
40
- if (!sessionId) throw new Error(`initialize failed HTTP ${response.status}: ${raw.slice(0, 2000)}`);
41
- parseSseJson(raw);
42
- return { sessionId, headers };
43
- }
44
-
45
- async function callNeusContext(baseUrl, sessionId, headers) {
46
- const response = await fetch(`${baseUrl}/mcp`, {
47
- method: 'POST',
48
- headers: { ...headers, 'mcp-session-id': sessionId },
49
- body: JSON.stringify({
50
- jsonrpc: '2.0',
51
- id: 2,
52
- method: 'tools/call',
53
- params: { name: 'neus_context', arguments: {} }
54
- })
55
- });
56
- const raw = await response.text();
57
- const payload = parseSseJson(raw);
58
- const text = payload?.result?.content?.[0]?.text;
59
- if (!text) throw new Error(`no neus_context body: ${raw.slice(0, 2000)}`);
60
- return JSON.parse(text);
61
- }
62
-
63
- async function close(baseUrl, sessionId, headers) {
64
- try {
65
- await fetch(`${baseUrl}/mcp`, {
66
- method: 'DELETE',
67
- headers: { ...headers, 'mcp-session-id': sessionId }
68
- });
69
- } catch {
70
- /* ignore */
71
- }
72
- }
73
-
74
- async function main() {
75
- let sessionId;
76
- let headers;
77
- try {
78
- ({ sessionId, headers } = await initMcpSession(BASE_URL));
79
- const ctx = await callNeusContext(BASE_URL, sessionId, headers);
80
- const summary = {
81
- mode: ctx.mode,
82
- headline: ctx.product?.headline,
83
- goldenPath: ctx.goldenPath,
84
- jobIntents: Array.isArray(ctx.jobs) ? ctx.jobs.map((j) => j.intent) : [],
85
- verifierCount: Array.isArray(ctx.verifierSummary) ? ctx.verifierSummary.length : null,
86
- urls: ctx.setup
87
- ? {
88
- mcp: ctx.setup.mcpEndpoint,
89
- profile: ctx.setup.profileUrl,
90
- accessKeys: ctx.setup.accessKeysUrl
91
- }
92
- : null
93
- };
94
- console.log(JSON.stringify(summary, null, 2));
95
- } finally {
96
- if (sessionId && headers) await close(BASE_URL, sessionId, headers);
97
- }
98
- }
99
-
100
- main().catch((e) => {
101
- console.error(e);
102
- process.exit(1);
103
- });
@@ -1,195 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Static contract guard for the hosted NEUS MCP surface.
5
- * Fails on drift across server.json, README, docs, and core server framing.
6
- */
7
-
8
- import { readFileSync, existsSync } from 'node:fs';
9
- import { dirname, join } from 'node:path';
10
- import { fileURLToPath } from 'node:url';
11
-
12
- const __dirname = dirname(fileURLToPath(import.meta.url));
13
-
14
- const MCP_DIR = __dirname;
15
-
16
- /** Final public tool surface (order must match server.json and hosted registration). */
17
- const PUBLIC_TOOLS = [
18
- 'neus_context',
19
- 'neus_verifiers_catalog',
20
- 'neus_proofs_check',
21
- 'neus_verify',
22
- 'neus_verify_or_guide',
23
- 'neus_proofs_get',
24
- 'neus_me',
25
- 'neus_agent_link',
26
- 'neus_agent_create'
27
- ];
28
-
29
- const NEUS_CONTEXT_TOP_LEVEL = [
30
- 'product',
31
- 'setup',
32
- 'mode',
33
- 'goldenPath',
34
- 'jobs',
35
- 'tools',
36
- 'proofModel',
37
- 'agentModel',
38
- 'safetyRules',
39
- 'verifierSummary'
40
- ];
41
-
42
- /** Required words in MCP package README (minimal teaching surface). */
43
- const README_REQUIRED = [
44
- 'neus_context',
45
- 'neus_verifiers_catalog',
46
- 'neus_proofs_check',
47
- 'neus_verify_or_guide',
48
- 'neus_verify',
49
- 'neus_proofs_get',
50
- 'neus_me',
51
- 'neus_agent_create',
52
- 'neus_agent_link',
53
- 'call neus_context first'
54
- ];
55
-
56
- /** Core teaching pages that must retain `neus_proofs_get` mention. */
57
- const NETWORK_DOC_FILES = ['overview.mdx', 'tools.mdx', 'setup.mdx', 'proofs-get.mdx'];
58
-
59
- /** Banned in public MCP-facing manifest + hosted package README (adapter boundary). */
60
- const BANNED_PUBLIC_SUBSTR = ['Zeus', 'Hostinger', 'trust layer', 'Verification MCP', 'runtime ledger', 'receipt chain'];
61
-
62
- function fail(msg) {
63
- console.error(`\nPUBLIC MCP CONTRACT FAILURE: ${msg}\n`);
64
- process.exit(1);
65
- }
66
-
67
- function readUtf8(rel) {
68
- return readFileSync(join(MCP_DIR, rel), 'utf8');
69
- }
70
-
71
- /** Slice of server.js covering public TOOLS[] entries (exclusive of extended tools). */
72
- function getPublicToolsDocumentationBlock(serverJs) {
73
- const start = serverJs.indexOf('const TOOLS = [');
74
- if (start < 0) return null;
75
- const zeus = serverJs.indexOf("name: 'zeus_url_fetch'", start);
76
- if (zeus < 0) return null;
77
- return serverJs.slice(start, zeus);
78
- }
79
-
80
- /** First tool-level description string before inputSchema for a named TOOLS entry. */
81
- function extractToolDescriptionFromToolsBlock(block, toolName) {
82
- const needle = `name: '${toolName}'`;
83
- const idx = block.indexOf(needle);
84
- if (idx < 0) return null;
85
- const fromName = block.slice(idx);
86
- const schemaAt = fromName.indexOf('inputSchema:');
87
- if (schemaAt < 0) return null;
88
- const head = fromName.slice(0, schemaAt);
89
- const multiline = head.match(/description:\s*\n\s*'((?:[^'\\]|\\.)*)'/);
90
- const single = head.match(/description:\s*'((?:[^'\\]|\\.)*)'/);
91
- const raw = multiline ? multiline[1] : single ? single[1] : null;
92
- return raw;
93
- }
94
-
95
- function main() {
96
- const serverJson = JSON.parse(readUtf8('server.json'));
97
- const readme = readUtf8('README.md');
98
- const serverJs = readUtf8('server.js');
99
-
100
- const title = serverJson.title || '';
101
- if (title.includes('Verification MCP')) fail('server.json title must not contain "Verification MCP".');
102
-
103
- /** Official MCP Registry rejects longer server summaries (422). */
104
- const REGISTRY_SERVER_DESCRIPTION_MAX = 100;
105
- const serverDesc = String(serverJson.description || '');
106
- if (serverDesc.length > REGISTRY_SERVER_DESCRIPTION_MAX) {
107
- fail(
108
- `server.json top-level description must be <= ${REGISTRY_SERVER_DESCRIPTION_MAX} chars for MCP Registry (got ${serverDesc.length})`
109
- );
110
- }
111
-
112
- if (!Array.isArray(serverJson.tools)) fail('server.json tools must be an array');
113
-
114
- const names = serverJson.tools.map((t) => t?.name).filter(Boolean);
115
- if (names.length !== PUBLIC_TOOLS.length) fail(`server.json must expose exactly ${PUBLIC_TOOLS.length} tools`);
116
- const setErr = PUBLIC_TOOLS.some((n, i) => names[i] !== n);
117
- if (setErr) {
118
- fail(
119
- `server.json tool order/set mismatch.\nExpected: ${PUBLIC_TOOLS.join(', ')}\nActual: ${names.join(', ')}`
120
- );
121
- }
122
-
123
- const extra = names.filter((n) => !PUBLIC_TOOLS.includes(n));
124
- if (extra.length) fail(`server.json has unexpected tools: ${extra.join(', ')}`);
125
-
126
- const sjPlain = JSON.stringify(serverJson);
127
- if (/\b[Hh]ub\b/.test(sjPlain)) fail('server.json must not use public "Hub" language');
128
-
129
- for (const banned of BANNED_PUBLIC_SUBSTR) {
130
- if (banned !== 'Verification MCP' && sjPlain.includes(banned)) fail(`server.json must not contain: ${banned}`);
131
- }
132
-
133
- if (/\b[Hh]ub\b/.test(readme)) fail('mcp/README.md must not use public "Hub" language');
134
-
135
- for (const needle of README_REQUIRED) {
136
- if (!readme.includes(needle)) fail(`mcp/README.md must mention: ${needle}`);
137
- }
138
-
139
- const repoRoot = join(MCP_DIR, '..', '..');
140
- const networkMcpDocs = join(repoRoot, 'network', 'docs', 'mcp');
141
- if (!existsSync(networkMcpDocs)) {
142
- fail(`network MCP docs directory missing (expected ${networkMcpDocs})`);
143
- }
144
-
145
- for (const doc of NETWORK_DOC_FILES) {
146
- const p = join(networkMcpDocs, doc);
147
- if (!existsSync(p)) fail(`Missing required doc: ${p}`);
148
- const text = readFileSync(p, 'utf8');
149
- if (!text.includes('neus_proofs_get')) fail(`${doc} must document neus_proofs_get`);
150
- if (/\b[Hh]ub\b/.test(text)) fail(`${doc} must not use public "Hub" language`);
151
- if (text.includes('Zeus')) fail(`${doc} must not expose "Zeus"`);
152
- }
153
-
154
- if (!/call neus_context first/i.test(serverJs)) {
155
- fail('server.js MCP instructions must tell models to call neus_context first');
156
- }
157
-
158
- const bpStart = serverJs.indexOf('function buildPublicNeusContextPayload(');
159
- if (bpStart < 0) fail('server.js must define buildPublicNeusContextPayload');
160
- const bpDelim = '// ERROR REMEDIATION';
161
- const bpEnd = serverJs.indexOf(bpDelim, bpStart);
162
- if (bpEnd < 0) fail('cannot locate buildPublicNeusContextPayload boundary before ERROR_REMEDIATION');
163
- const bpSlice = serverJs.slice(bpStart, bpEnd);
164
-
165
- for (const key of NEUS_CONTEXT_TOP_LEVEL) {
166
- if (key === 'verifierSummary') {
167
- if (!/\bverifierSummary\b/.test(bpSlice)) fail('buildPublicNeusContextPayload must define verifierSummary');
168
- } else if (!bpSlice.includes(`${key}:`)) {
169
- fail(`buildPublicNeusContextPayload must define ${key}`);
170
- }
171
- }
172
- const noise = ['defaultCallOrder:', 'routing:', 'toolSelection:', 'responseRules:', 'catalogMeta', 'billing:', 'integrationGuidelines'];
173
- for (const bad of noise) {
174
- if (bpSlice.includes(bad)) fail(`buildPublicNeusContextPayload must not carry legacy section: ${bad}`);
175
- }
176
- if (/\bzeus\b/i.test(bpSlice)) fail('buildPublicNeusContextPayload must not mention Zeus');
177
- if (/\bhub\b/i.test(bpSlice)) fail('buildPublicNeusContextPayload must not mention Hub wording');
178
-
179
- const toolsDoc = getPublicToolsDocumentationBlock(serverJs);
180
- if (!toolsDoc) fail('Cannot slice public TOOLS block before zeus_url_fetch');
181
- for (const toolName of PUBLIC_TOOLS) {
182
- const fromJs = extractToolDescriptionFromToolsBlock(toolsDoc, toolName);
183
- const fromJson = serverJson.tools.find((t) => t.name === toolName)?.description;
184
- if (fromJs == null) fail(`Parse TOOLS[].description failed for ${toolName}`);
185
- if (fromJs !== fromJson) {
186
- fail(
187
- `server.json tool "${toolName}" description must equal TOOLS in server.js.\nmanifest: ${JSON.stringify(fromJson)}\nserver.js: ${JSON.stringify(fromJs)}`
188
- );
189
- }
190
- }
191
-
192
- console.log('PUBLIC MCP CONTRACT OK');
193
- }
194
-
195
- main();