generate-readme.js•14.3 kB
#!/usr/bin/env node
/**
* Generate and update README.md with installation links and tools documentation
*/
import fs from "fs";
import path from "path";
import { execSync } from "child_process";
import { fileURLToPath } from "url";
import { dirname } from "path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const readmePath = path.join(__dirname, "..", "README.md");
/**
* Update a section in the README between markers
*/
function updateSection(content, startMarker, endMarker, newContent) {
const startIndex = content.indexOf(startMarker);
const endIndex = content.indexOf(endMarker);
if (startIndex === -1 || endIndex === -1) {
console.warn(`Warning: Markers not found for section between "${startMarker}" and "${endMarker}"`);
return content;
}
return [content.slice(0, startIndex + startMarker.length), "\n", newContent, "\n", content.slice(endIndex)].join("");
}
/**
* Load tools configuration and generate documentation
*/
function generateToolsDocs() {
const toolsConfigPath = path.join(__dirname, "..", "tools-config.json");
const tools = JSON.parse(fs.readFileSync(toolsConfigPath, "utf-8"));
// Group tools by category
const toolCategories = {
"Collection Management": ["list_collections", "create_collection"],
"Asset Management": ["list_assets", "create_asset"],
};
function formatParameter(name, param, required = false) {
const parts = [];
parts.push(` - \`${name}\``);
const meta = [];
if (param.type) meta.push(param.type);
if (param.enum) meta.push(`enum: ${param.enum.join(", ")}`);
if (!required) meta.push("optional");
else meta.push("required");
if (meta.length > 0) parts.push(` (${meta.join(", ")})`);
parts.push(`: ${param.description || "No description"}`);
const constraints = [];
if (param.minLength !== undefined) constraints.push(`min length: ${param.minLength}`);
if (param.maxLength !== undefined) constraints.push(`max length: ${param.maxLength}`);
if (param.minimum !== undefined) constraints.push(`min: ${param.minimum}`);
if (param.maximum !== undefined) constraints.push(`max: ${param.maximum}`);
if (param.default !== undefined) constraints.push(`default: ${JSON.stringify(param.default)}`);
if (constraints.length > 0) parts.push(` [${constraints.join(", ")}]`);
return parts.join("");
}
function generateExample(tool) {
const examples = {
uranium_list_collections: `uranium_list_collections()`,
uranium_list_assets: `uranium_list_assets({
contractId: "collection_id_here",
quickFilter: "search text",
page: 1,
pageSize: 20
})`,
uranium_create_collection: `uranium_create_collection({
name: "My NFT Collection",
symbol: "MNC",
type: "ERC721"
})`,
uranium_create_asset: `uranium_create_asset({
contractId: "collection_id_here",
title: "My NFT Title",
filePath: "/absolute/path/to/image.jpg",
description: "Beautiful digital art",
shareWithCommunity: true
})`,
};
return examples[tool.name] || "";
}
function formatTool(tool) {
const lines = [];
lines.push(`#### ${tool.name}`);
lines.push(`- **Description**: ${tool.description}`);
const params = tool.inputSchema.properties || {};
const required = tool.inputSchema.required || [];
if (Object.keys(params).length === 0) {
lines.push("- **Parameters**: None");
} else {
lines.push("- **Parameters**:");
for (const [name, param] of Object.entries(params)) {
lines.push(formatParameter(name, param, required.includes(name)));
}
}
const example = generateExample(tool);
if (example) {
lines.push("- **Example**:");
lines.push("```typescript");
lines.push(example);
lines.push("```");
}
return lines.join("\n");
}
const lines = [];
lines.push("<!--- Tools generated by utils/generate-tools-docs.js -->");
lines.push("");
lines.push("This MCP server provides 4 main tools for NFT management:");
lines.push("");
for (const [category, toolNames] of Object.entries(toolCategories)) {
lines.push("<details>");
lines.push(`<summary><b>${category}</b></summary>`);
lines.push("");
for (const toolName of toolNames) {
const tool = tools.find((t) => t.name === toolName);
if (tool) {
lines.push(formatTool(tool));
lines.push("");
}
}
lines.push("</details>");
lines.push("");
}
lines.push("<!--- End of tools generated section -->");
return lines.join("\n");
}
/**
* Generate the Getting Started section with installation links
*/
function generateGettingStartedSection() {
// Run generate-links.js and capture output
const generateLinksPath = path.join(__dirname, "generate-links.js");
const output = execSync(`node ${generateLinksPath}`).toString();
const lines = output.split(/\r?\n/).filter(Boolean);
const vscodeUrl = lines[0];
const insidersUrl = lines[1];
const cursorUrl = lines[2];
const gooseUrl = lines[3];
const lmStudioUrl = lines[4];
const section = `First, install the Uranium MCP server with your client.
**Standard config** works in most of the tools:
\`\`\`json
{
"mcpServers": {
"uranium": {
"command": "npx",
"args": ["uranium-tools-mcp@latest"],
"env": {
"URANIUM_API_KEY": "your_api_key_here"
}
}
}
}
\`\`\`
<details>
<summary>Claude Code</summary>
Use the Claude Code CLI to add the Uranium MCP server:
\`\`\`bash
claude mcp add uranium npx uranium-tools-mcp@latest
\`\`\`
Then set your URANIUM_API_KEY when prompted.
</details>
<details>
<summary>Claude Desktop</summary>
Add to your Claude Desktop configuration file:
- macOS: \`~/Library/Application Support/Claude/claude_desktop_config.json\`
- Windows: \`%APPDATA%\\Claude\\claude_desktop_config.json\`
- Linux: \`~/.config/Claude/claude_desktop_config.json\`
\`\`\`json
{
"mcpServers": {
"uranium": {
"command": "npx",
"args": ["uranium-tools-mcp@latest"],
"env": {
"URANIUM_API_KEY": "your_api_key_here"
}
}
}
}
\`\`\`
</details>
<details>
<summary>Codex</summary>
Create or edit the configuration file \`~/.codex/config.toml\` and add:
\`\`\`toml
[mcp_servers.uranium]
command = "npx"
args = ["uranium-tools-mcp@latest"]
env = { URANIUM_API_KEY = "your_api_key_here" }
\`\`\`
For more information, see the [Codex MCP documentation](https://github.com/openai/codex/blob/main/codex-rs/config.md#mcp_servers).
</details>
<details>
<summary>Cursor</summary>
#### Click the button to install:
[<img src="https://cursor.com/deeplink/mcp-install-dark.svg" alt="Install in Cursor">](${cursorUrl})
#### Or install manually:
Go to \`Cursor Settings\` -> \`MCP\` -> \`Add new MCP Server\`. Name it "uranium", use \`command\` type with the command \`npx uranium-tools-mcp@latest\`. Add environment variable \`URANIUM_API_KEY\` with your API key.
</details>
<details>
<summary>Gemini CLI</summary>
Follow the MCP install [guide](https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md#configure-the-mcp-server-in-settingsjson), use the standard config above with your API key.
</details>
<details>
<summary>Goose</summary>
#### Click the button to install:
[](${gooseUrl})
#### Or install manually:
Go to \`Advanced settings\` -> \`Extensions\` -> \`Add custom extension\`. Name it "uranium", use type \`STDIO\`, and set the \`command\` to \`npx uranium-tools-mcp@latest\`. Add environment variable \`URANIUM_API_KEY\` with your API key. Click "Add Extension".
</details>
<details>
<summary>LM Studio</summary>
#### Click the button to install:
[](${lmStudioUrl})
#### Or install manually:
Go to \`Program\` in the right sidebar -> \`Install\` -> \`Edit mcp.json\`. Use the standard config above with your API key.
</details>
<details>
<summary>opencode</summary>
Follow the MCP Servers [documentation](https://opencode.ai/docs/mcp-servers/). For example in \`~/.config/opencode/opencode.json\`:
\`\`\`json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"uranium": {
"type": "local",
"command": [
"npx",
"uranium-tools-mcp@latest"
],
"env": {
"URANIUM_API_KEY": "your_api_key_here"
},
"enabled": true
}
}
}
\`\`\`
</details>
<details>
<summary>Qodo Gen</summary>
Open [Qodo Gen](https://docs.qodo.ai/qodo-documentation/qodo-gen) chat panel in VSCode or IntelliJ → Connect more tools → + Add new MCP → Paste the standard config above with your API key.
Click \`Save\`.
</details>
<details>
<summary>VS Code</summary>
#### Click the button to install:
[<img src="https://img.shields.io/badge/VS_Code-VS_Code?style=flat-square&label=Install%20Server&color=0098FF" alt="Install in VS Code">](${vscodeUrl}) [<img alt="Install in VS Code Insiders" src="https://img.shields.io/badge/VS_Code_Insiders-VS_Code_Insiders?style=flat-square&label=Install%20Server&color=24bfa5">](${insidersUrl})
#### Or install manually:
Follow the MCP install [guide](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server), use the standard config above. You can also install the Uranium MCP server using the VS Code CLI:
\`\`\`bash
# For VS Code
code --add-mcp '{"name":"uranium","command":"npx","args":["uranium-tools-mcp@latest"],"env":{"URANIUM_API_KEY":"your_api_key_here"}}'
\`\`\`
After installation, the Uranium MCP server will be available for use with your GitHub Copilot agent in VS Code.
</details>
<details>
<summary>Windsurf</summary>
Follow Windsurf MCP [documentation](https://docs.windsurf.com/windsurf/cascade/mcp). Use the standard config above with your API key.
</details>`;
return section.trim();
}
/**
* Generate badges section for quick installation
*/
function generateBadgesSection() {
// Run generate-links.js and capture output
const generateLinksPath = path.join(__dirname, "generate-links.js");
const output = execSync(`node ${generateLinksPath}`).toString();
const lines = output.split(/\r?\n/).filter(Boolean);
const vscodeUrl = lines[0];
const insidersUrl = lines[1];
const cursorUrl = lines[2];
const gooseUrl = lines[3];
const lmStudioUrl = lines[4];
return `<!-- BADGES:START -->
### 🚀 Quick Install
[<img src="https://img.shields.io/badge/VS_Code-0098FF?style=flat-square&logo=visual-studio-code&logoColor=white" alt="Install in VS Code">](${vscodeUrl}) [<img src="https://img.shields.io/badge/VS_Code_Insiders-24bfa5?style=flat-square&logo=visual-studio-code&logoColor=white" alt="Install in VS Code Insiders">](${insidersUrl}) [<img src="https://img.shields.io/badge/Cursor-000000?style=flat-square&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTggMEMzLjU4IDAgMCAzLjU4IDAgOFMzLjU4IDE2IDggMTZTMTYgMTIuNDIgMTYgOFMxMi40MiAwIDggMFpNOCAxNEM0LjY5IDE0IDIgMTEuMzEgMiA4UzQuNjkgMiA4IDJDOC41NSAyIDkuMDggMi4wOSA5LjU4IDIuMjRMOCA0TDEyIDhMOCAxMlY5TDUgMTJWNEw4IDdWNEw5LjU4IDIuMjRDMTIuODQgMy4yNCAxNSA1LjQgMTUgOEMxNSAxMS4zMSAxMS4zMSAxNCA4IDE0WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+" alt="Install in Cursor">](${cursorUrl}) [<img src="https://img.shields.io/badge/Goose-FFA500?style=flat-square&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTggMEMzLjU4IDAgMCAzLjU4IDAgOFMzLjU4IDE2IDggMTZTMTYgMTIuNDIgMTYgOFMxMi40MiAwIDggMFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPg==" alt="Install in Goose">](${gooseUrl}) [<img src="https://img.shields.io/badge/LM_Studio-7B68EE?style=flat-square&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTggMEwxNiA0VjEyTDggMTZMMCAxMlY0TDggMFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPg==" alt="Install in LM Studio">](${lmStudioUrl})
<!-- BADGES:END -->`;
}
/**
* Main function to update README
*/
function updateReadme() {
try {
// Read current README
let readme = fs.readFileSync(readmePath, "utf-8");
// Update or insert badges section after the main heading
const badgesSection = generateBadgesSection();
// Remove existing badges section if present
readme = readme.replace(/<!-- BADGES:START -->[\s\S]*?<!-- BADGES:END -->/g, "");
// Find the first heading and insert badges after it
const headingMatch = readme.match(/^# .+$/m);
if (headingMatch) {
const headingIndex = readme.indexOf(headingMatch[0]);
const headingEndIndex = headingIndex + headingMatch[0].length;
readme = readme.slice(0, headingEndIndex) + "\n\n" + badgesSection + readme.slice(headingEndIndex);
console.log("✓ Updated badges section");
} else {
console.warn("⚠ Could not find main heading for badges");
}
// Update Getting Started section
const gettingStartedSection = generateGettingStartedSection();
readme = updateSection(
readme,
"<!--\n// Generate using:\nnode utils/generate-readme.js\n-->\n\n### Getting Started",
"### Configuration",
gettingStartedSection + "\n\n",
);
if (readme.includes(gettingStartedSection)) {
console.log("✓ Updated Getting Started section");
} else {
console.warn("⚠ Could not update Getting Started section");
}
// Update Tools section
const toolsDocs = generateToolsDocs();
readme = updateSection(readme, "### Tools", "### Supported File Formats", "\n\n" + toolsDocs + "\n\n");
if (readme.includes(toolsDocs)) {
console.log("✓ Updated Tools section");
} else {
console.warn("⚠ Could not update Tools section");
}
// Write updated README
fs.writeFileSync(readmePath, readme, "utf-8");
console.log("✅ README.md updated successfully");
} catch (error) {
console.error("Error updating README:", error);
process.exit(1);
}
}
// Run if called directly
updateReadme();