init project
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import { z } from 'zod';
|
||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { errorResult, jsonResult, textResult, toErrorMessage } from '../shared.js';
|
||||
|
||||
async function fetchJson(url: string): Promise<unknown> {
|
||||
const response = await fetch(url, { headers: { Accept: 'application/json' } });
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status} from ${url}`);
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async function fetchText(url: string): Promise<string> {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status} from ${url}`);
|
||||
}
|
||||
return response.text();
|
||||
}
|
||||
|
||||
interface NpmPackageInfo {
|
||||
'dist-tags': { latest: string };
|
||||
versions: Record<string, { repository?: { url?: string }; homepage?: string; description?: string }>;
|
||||
readme?: string;
|
||||
}
|
||||
|
||||
async function fetchNpmDocs(name: string, version?: string): Promise<Record<string, unknown>> {
|
||||
const registryUrl = `https://registry.npmjs.org/${encodeURIComponent(name)}`;
|
||||
const info = (await fetchJson(registryUrl)) as NpmPackageInfo;
|
||||
const resolvedVersion = version ?? info['dist-tags'].latest;
|
||||
const versionData = info.versions[resolvedVersion];
|
||||
|
||||
let readme: string | null = null;
|
||||
try {
|
||||
readme = await fetchText(`https://unpkg.com/${name}@${resolvedVersion}/README.md`);
|
||||
} catch {
|
||||
try {
|
||||
readme = await fetchText(`https://unpkg.com/${name}@${resolvedVersion}/readme.md`);
|
||||
} catch {
|
||||
readme = info.readme ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
ecosystem: 'npm',
|
||||
package: name,
|
||||
version: resolvedVersion,
|
||||
description: versionData?.description ?? null,
|
||||
homepage: versionData?.homepage ?? null,
|
||||
repository: versionData?.repository?.url ?? null,
|
||||
readme,
|
||||
readmeSource: readme ? `https://unpkg.com/${name}@${resolvedVersion}/README.md` : null,
|
||||
};
|
||||
}
|
||||
|
||||
interface PackagistPackageInfo {
|
||||
package: {
|
||||
name: string;
|
||||
description?: string;
|
||||
repository?: string;
|
||||
versions?: Record<string, { source?: { url?: string }; homepage?: string }>;
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchPackagistDocs(name: string, version?: string): Promise<Record<string, unknown>> {
|
||||
const url = `https://packagist.org/packages/${encodeURIComponent(name)}.json`;
|
||||
const info = (await fetchJson(url)) as PackagistPackageInfo;
|
||||
const pkg = info.package;
|
||||
const versions = Object.keys(pkg.versions ?? {}).sort();
|
||||
const resolvedVersion = version ?? versions[versions.length - 1];
|
||||
|
||||
return {
|
||||
ecosystem: 'composer',
|
||||
package: pkg.name,
|
||||
version: resolvedVersion,
|
||||
description: pkg.description ?? null,
|
||||
repository: pkg.repository ?? null,
|
||||
versions: versions.slice(-20),
|
||||
note: 'README fetching for Packagist packages is not yet implemented; use the repository URL to read the README.',
|
||||
};
|
||||
}
|
||||
|
||||
export function registerSearchPackageDocsTool(server: McpServer): void {
|
||||
server.registerTool(
|
||||
'search_package_docs',
|
||||
{
|
||||
title: 'Search Package Docs',
|
||||
description:
|
||||
'Fetch README/docs for an npm or Composer/Packagist package. For npm packages, resolves the version and fetches the README from unpkg. For Composer packages, returns Packagist metadata including the repository URL.',
|
||||
inputSchema: {
|
||||
name: z.string().min(1).describe('Package name, e.g. "axios" or "laravel/framework".'),
|
||||
ecosystem: z
|
||||
.enum(['npm', 'composer', 'auto'])
|
||||
.optional()
|
||||
.default('auto')
|
||||
.describe('Which package ecosystem to search. Auto detects from the name ("laravel/framework" → composer).'),
|
||||
version: z.string().optional().describe('Specific version to look up. Defaults to latest.'),
|
||||
},
|
||||
},
|
||||
async ({ name, ecosystem, version }) => {
|
||||
try {
|
||||
const resolvedEcosystem = ecosystem === 'auto' ? (name.includes('/') ? 'composer' : 'npm') : ecosystem;
|
||||
if (resolvedEcosystem === 'composer') {
|
||||
return jsonResult(await fetchPackagistDocs(name, version));
|
||||
}
|
||||
return jsonResult(await fetchNpmDocs(name, version));
|
||||
} catch (error) {
|
||||
return errorResult(toErrorMessage(error));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user