@sgpdirectory/mcp 1.2.0 → 1.3.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.
- package/.mise.toml +3 -0
- package/README.md +4 -4
- package/package.json +13 -8
- package/scripts/0-install-mcp-publisher.sh +44 -0
- package/scripts/1-gen-key.sh +0 -0
- package/scripts/2-gen-dns.sh +0 -0
- package/scripts/{3-login-dns.sh → 3-publish-mcp.sh} +7 -2
- package/scripts/integration-test.js +65 -0
- package/scripts/smoke-test.js +58 -76
- package/server.json +4 -4
- package/scripts/4-publish.sh +0 -13
package/.mise.toml
ADDED
package/README.md
CHANGED
|
@@ -50,21 +50,21 @@ If your proxy is behind an access token, you can pass it via `SGP_DIRECTORY_API_
|
|
|
50
50
|
1. **Login to NPM**
|
|
51
51
|
Run the login command and follow the prompts. You need to be a part of the `@sgpdirectory` org.
|
|
52
52
|
```bash
|
|
53
|
-
|
|
53
|
+
pnpm login
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
2. **Publish the package**
|
|
57
57
|
Run the following inside your wrapper directory:
|
|
58
58
|
```bash
|
|
59
|
-
|
|
59
|
+
pnpm publish --access public
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
## Local Testing
|
|
63
63
|
If you have cloned the repository locally, you can run:
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
pnpm install
|
|
67
|
+
pnpm start
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
This will launch the proxy and wait for standard `stdio` JSON-RPC messages.
|
package/package.json
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sgpdirectory/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Singapore business directory. Search companies, UENs, and SSIC industry classifications.",
|
|
5
5
|
"main": "index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"start": "node ./index.js",
|
|
8
|
-
"test": "node scripts/smoke-test.js",
|
|
9
|
-
"version": "node scripts/sync-version.js",
|
|
10
|
-
"deploy": "sh scripts/4-publish.sh"
|
|
11
|
-
},
|
|
12
6
|
"keywords": [
|
|
13
7
|
"mcp",
|
|
14
8
|
"mcp-server",
|
|
@@ -31,5 +25,16 @@
|
|
|
31
25
|
"publishConfig": {
|
|
32
26
|
"access": "public"
|
|
33
27
|
},
|
|
34
|
-
"mcpName": "com.sgpdirectory/
|
|
28
|
+
"mcpName": "com.sgpdirectory/mcp",
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@modelcontextprotocol/sdk": "^1.27.1"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"start": "node ./index.js",
|
|
34
|
+
"version": "node scripts/sync-version.js",
|
|
35
|
+
"deploy:npm": "pnpm login && pnpm publish --access public --no-git-checks",
|
|
36
|
+
"deploy:mcp": "sh scripts/3-publish-mcp.sh",
|
|
37
|
+
"test:integration": "node scripts/integration-test.js",
|
|
38
|
+
"test:smoke": "node scripts/smoke-test.js"
|
|
39
|
+
}
|
|
35
40
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Install the latest mcp-publisher tool from modelcontextprotocol/registry
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
# Detect OS and architecture
|
|
6
|
+
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
|
7
|
+
ARCH=$(uname -m)
|
|
8
|
+
|
|
9
|
+
if [ "$ARCH" = "x86_64" ]; then
|
|
10
|
+
ARCH="amd64"
|
|
11
|
+
elif [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
|
|
12
|
+
ARCH="arm64"
|
|
13
|
+
else
|
|
14
|
+
echo "Unsupported architecture: $ARCH"
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
TAR_GZ_NAME="mcp-publisher_${OS}_${ARCH}.tar.gz"
|
|
19
|
+
DOWNLOAD_URL="https://github.com/modelcontextprotocol/registry/releases/latest/download/$TAR_GZ_NAME"
|
|
20
|
+
|
|
21
|
+
echo "Downloading $TAR_GZ_NAME..."
|
|
22
|
+
TMP_DIR=$(mktemp -d)
|
|
23
|
+
curl -sL -o "$TMP_DIR/$TAR_GZ_NAME" "$DOWNLOAD_URL"
|
|
24
|
+
|
|
25
|
+
echo "Extracting..."
|
|
26
|
+
tar -xzf "$TMP_DIR/$TAR_GZ_NAME" -C "$TMP_DIR"
|
|
27
|
+
|
|
28
|
+
INSTALL_DIR="$HOME/.local/bin"
|
|
29
|
+
mkdir -p "$INSTALL_DIR"
|
|
30
|
+
echo "Installing mcp-publisher to $INSTALL_DIR..."
|
|
31
|
+
mv "$TMP_DIR/mcp-publisher" "$INSTALL_DIR/"
|
|
32
|
+
chmod +x "$INSTALL_DIR/mcp-publisher"
|
|
33
|
+
|
|
34
|
+
# Cleanup
|
|
35
|
+
rm -rf "$TMP_DIR"
|
|
36
|
+
|
|
37
|
+
echo "mcp-publisher has been installed."
|
|
38
|
+
if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
|
|
39
|
+
echo ""
|
|
40
|
+
echo "WARNING: $INSTALL_DIR is not in your PATH."
|
|
41
|
+
echo "To use the mcp-publisher command globally, please add the following line to your shell profile (~/.bashrc, ~/.zshrc, etc.):"
|
|
42
|
+
echo ""
|
|
43
|
+
echo "export PATH=\"\$PATH:$INSTALL_DIR\""
|
|
44
|
+
fi
|
package/scripts/1-gen-key.sh
CHANGED
|
File without changes
|
package/scripts/2-gen-dns.sh
CHANGED
|
File without changes
|
|
@@ -5,8 +5,13 @@ KEY_FILE="/tmp/sgpdirectory-mcp-dns-key.pem"
|
|
|
5
5
|
# Generate keypair
|
|
6
6
|
#openssl genpkey -algorithm Ed25519 -out $KEY_FILE
|
|
7
7
|
|
|
8
|
-
# Get public key for DNS record
|
|
8
|
+
# Get public key for DNS record, add to your DNS provider
|
|
9
9
|
#echo "$DOMAIN. IN TXT \"v=MCPv1; k=ed25519; p=$(openssl pkey -in $KEY_FILE -pubout -outform DER | tail -c 32 | base64)\""
|
|
10
10
|
|
|
11
|
-
#
|
|
11
|
+
# Login to MCP Registry
|
|
12
12
|
mcp-publisher login dns --domain $DOMAIN --private-key $(openssl pkey -in $KEY_FILE -noout -text | grep -A3 "priv:" | tail -n +2 | tr -d ' :\n')
|
|
13
|
+
|
|
14
|
+
# Publish to MCP Registry
|
|
15
|
+
echo "Publishing to registry.modelcontextprotocol.io..."
|
|
16
|
+
# mcp-publisher publish uses the server.json file to update the MCP registry
|
|
17
|
+
mcp-publisher publish
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @sgpdirectory/mcp integration test
|
|
4
|
+
* Uses the official MCP SDK to verify the local server functionality.
|
|
5
|
+
*/
|
|
6
|
+
const { Client } = require("@modelcontextprotocol/sdk/client/index.js");
|
|
7
|
+
const { StdioClientTransport } = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
async function runIntegrationTest() {
|
|
11
|
+
const scriptPath = path.resolve(__dirname, '../index.js');
|
|
12
|
+
|
|
13
|
+
console.log('--- Starting SGP Directory MCP Integration Test (SDK Mode) ---');
|
|
14
|
+
|
|
15
|
+
const transport = new StdioClientTransport({
|
|
16
|
+
command: "node",
|
|
17
|
+
args: [scriptPath],
|
|
18
|
+
env: {
|
|
19
|
+
...process.env,
|
|
20
|
+
SGP_DIRECTORY_API_KEY: process.env.SGP_DIRECTORY_API_KEY || 'test-integration-key'
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const client = new Client({
|
|
25
|
+
name: "integration-test-client",
|
|
26
|
+
version: "1.3.1"
|
|
27
|
+
}, {
|
|
28
|
+
capabilities: {}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
console.log("1. Connecting to local MCP server...");
|
|
33
|
+
await client.connect(transport);
|
|
34
|
+
console.log("✅ Connected");
|
|
35
|
+
|
|
36
|
+
console.log("2. Listing tools...");
|
|
37
|
+
const toolsRes = await client.listTools();
|
|
38
|
+
const tools = toolsRes.tools || [];
|
|
39
|
+
console.log(`✅ Found ${tools.length} tools: ${tools.map(t => t.name).join(', ')}`);
|
|
40
|
+
|
|
41
|
+
if (tools.length === 0) throw new Error("No tools found!");
|
|
42
|
+
|
|
43
|
+
console.log('3. Calling "search_entities" with query "Google"...');
|
|
44
|
+
const result = await client.callTool({
|
|
45
|
+
name: "search_entities",
|
|
46
|
+
arguments: { query: "Google", limit: 1 }
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (result.isError) {
|
|
50
|
+
throw new Error(`Tool call returned error: ${JSON.stringify(result.content)}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log('✅ Search call responded successfully');
|
|
54
|
+
|
|
55
|
+
console.log('\n✨ ALL INTEGRATION TESTS PASSED');
|
|
56
|
+
await client.close();
|
|
57
|
+
process.exit(0);
|
|
58
|
+
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.error(`\n❌ INTEGRATION TEST FAILED: ${err.message}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
runIntegrationTest();
|
package/scripts/smoke-test.js
CHANGED
|
@@ -1,93 +1,75 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* @sgpdirectory/mcp smoke test
|
|
4
|
-
*
|
|
5
|
-
* to verify that the server starts up and responds correctly.
|
|
4
|
+
* Uses the official MCP SDK to verify the published package.
|
|
6
5
|
*/
|
|
7
|
-
const {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
6
|
+
const { Client } = require("@modelcontextprotocol/sdk/client/index.js");
|
|
7
|
+
const { StdioClientTransport } = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
8
|
+
const pkg = require('../package.json');
|
|
9
|
+
|
|
10
|
+
async function runSmokeTest() {
|
|
11
|
+
// 1. Determine version (arg or package.json)
|
|
12
|
+
const specifiedVersion = process.argv[2];
|
|
13
|
+
const version = specifiedVersion || pkg.version;
|
|
14
|
+
const packageName = pkg.name;
|
|
15
|
+
|
|
16
|
+
console.log(`--- Starting SGP Directory MCP Smoke Test (SDK Mode) ---`);
|
|
17
|
+
console.log(`Testing package: ${packageName}@${version}`);
|
|
18
|
+
|
|
19
|
+
// We use the canonical way to run a binary when it doesn't match the package name:
|
|
20
|
+
// npx -y --package @scope/pkg@version -- binary-name
|
|
21
|
+
const transport = new StdioClientTransport({
|
|
22
|
+
command: "npx",
|
|
23
|
+
args: [
|
|
24
|
+
"-y",
|
|
25
|
+
"--package", `${packageName}@${version}`,
|
|
26
|
+
"--",
|
|
27
|
+
"sgpdirectory-mcp"
|
|
28
|
+
],
|
|
29
|
+
env: {
|
|
30
|
+
...process.env,
|
|
31
|
+
SGP_DIRECTORY_API_KEY: 'test-smoke-key'
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (process.env.DEBUG) {
|
|
36
|
+
transport.onmessage = (msg) => console.log(`[Transport JSON-RPC]: ${JSON.stringify(msg)}`);
|
|
21
37
|
}
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
let output = '';
|
|
25
|
-
let responseFound = false;
|
|
26
|
-
|
|
27
|
-
server.stdout.on('data', (data) => {
|
|
28
|
-
const str = data.toString();
|
|
29
|
-
output += str;
|
|
30
|
-
|
|
31
|
-
// Just log a snippet if too long
|
|
32
|
-
console.log(`[STDOUT snippet]: ${str.slice(0, 200).trim()}${str.length > 200 ? '...' : ''}`);
|
|
33
38
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
});
|
|
39
|
+
const client = new Client({
|
|
40
|
+
name: "smoke-test-client",
|
|
41
|
+
version: "1.0.0"
|
|
42
|
+
}, {
|
|
43
|
+
capabilities: {}
|
|
44
|
+
});
|
|
41
45
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
});
|
|
46
|
+
try {
|
|
47
|
+
console.log("Connecting to MCP server via npx...");
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
protocolVersion: '2024-11-05',
|
|
52
|
-
capabilities: {},
|
|
53
|
-
clientInfo: { name: 'smoke-test', version: '1.0.1' }
|
|
54
|
-
}
|
|
55
|
-
}) + '\n';
|
|
49
|
+
// Timeout for the whole connection process
|
|
50
|
+
const timeout = setTimeout(() => {
|
|
51
|
+
console.error("❌ Error: Smoke test timed out after 15s");
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}, 15000);
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
await client.connect(transport);
|
|
56
|
+
clearTimeout(timeout);
|
|
59
57
|
|
|
60
|
-
|
|
61
|
-
function cleanup() {
|
|
62
|
-
if (isCleanedUp) return;
|
|
63
|
-
isCleanedUp = true;
|
|
58
|
+
console.log("✅ Successfully connected and initialized");
|
|
64
59
|
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
// Verify we can at least list tools (standard health check)
|
|
61
|
+
const tools = await client.listTools();
|
|
62
|
+
console.log(`✅ Server reported ${tools.tools.length} available tools`);
|
|
67
63
|
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
console.log("\n✨ SMOKE TEST PASSED: Package is functional on the registry.");
|
|
65
|
+
await client.close();
|
|
70
66
|
process.exit(0);
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error("\n❌ SMOKE TEST FAILED");
|
|
70
|
+
console.error(error.message);
|
|
73
71
|
process.exit(1);
|
|
74
72
|
}
|
|
75
73
|
}
|
|
76
74
|
|
|
77
|
-
|
|
78
|
-
const timeout = setTimeout(() => {
|
|
79
|
-
console.log('\n--- Smoke Test Timeout (15s) ---');
|
|
80
|
-
cleanup();
|
|
81
|
-
}, 15000);
|
|
82
|
-
|
|
83
|
-
server.on('exit', (code, signal) => {
|
|
84
|
-
if (!isCleanedUp) {
|
|
85
|
-
console.log(`\n--- SGP Directory MCP exited unexpectedly (code: ${code}, signal: ${signal}) ---`);
|
|
86
|
-
cleanup();
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
server.on('error', (err) => {
|
|
91
|
-
console.error('\n❌ Failed to launch SGP Directory MCP:', err.message);
|
|
92
|
-
process.exit(1);
|
|
93
|
-
});
|
|
75
|
+
runSmokeTest();
|
package/server.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
-
"name": "com.sgpdirectory/
|
|
3
|
+
"name": "com.sgpdirectory/mcp",
|
|
4
4
|
"description": "Singapore business directory. Search companies, UENs, and SSIC industry classifications.",
|
|
5
5
|
"title": "Singapore Business Directory",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.3.1",
|
|
7
7
|
"websiteUrl": "https://sgpdirectory.com",
|
|
8
8
|
"icons": [
|
|
9
9
|
{
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"registryType": "npm",
|
|
36
36
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
37
37
|
"identifier": "@sgpdirectory/mcp",
|
|
38
|
-
"version": "1.
|
|
38
|
+
"version": "1.3.1",
|
|
39
39
|
"transport": {
|
|
40
40
|
"type": "stdio"
|
|
41
41
|
},
|
|
@@ -49,4 +49,4 @@
|
|
|
49
49
|
]
|
|
50
50
|
}
|
|
51
51
|
]
|
|
52
|
-
}
|
|
52
|
+
}
|
package/scripts/4-publish.sh
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# Ensure we're in the project root
|
|
4
|
-
cd "$(dirname "$0")/.."
|
|
5
|
-
|
|
6
|
-
# Publish to NPM
|
|
7
|
-
echo "📦 Publishing to npmjs.com..."
|
|
8
|
-
npm publish --access public
|
|
9
|
-
|
|
10
|
-
# Publish to MCP Registry
|
|
11
|
-
echo "🚀 Publishing to registry.modelcontextprotocol.io..."
|
|
12
|
-
# mcp-publisher publish uses the server.json file to update the MCP registry
|
|
13
|
-
mcp-publisher publish server.json
|