feat: MVP implementation — MCP server, partner one-pager, legal docs
Code (code/): - TypeScript MCP server with 5 tools (business info, hours, services, booking, related) - PostgreSQL schema with pilot business seed data - /.well-known/mcp-server manifest generator - Railway deployment config (Dockerfile, railway.json) - Multi-tenant gateway placeholder GTM (docs/gtm/): - Partner One-Pager: agency sales asset with economics, onboarding, competitive comparison Legal (docs/legal/): - Data Flow & Privacy: data collection, storage, sharing, GDPR/CCPA commitments - Partnership Agreement Skeleton: template for agency/CoC partnerships - IP Notes: well-known convention positioning, telemetry ownership, open-source strategy
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import { pool } from './db/schema.js';
|
||||
|
||||
interface ManifestConfig {
|
||||
businessSlug: string;
|
||||
serverBaseUrl: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the /.well-known/mcp-server JSON manifest.
|
||||
*
|
||||
* Businesses (or their agencies) upload this file to their website root
|
||||
* at `/.well-known/mcp-server`. AI agents discover it automatically.
|
||||
*/
|
||||
export function generateManifest(config: ManifestConfig): Record<string, any> {
|
||||
const { businessSlug, serverBaseUrl } = config;
|
||||
return {
|
||||
mcp_server: `${serverBaseUrl}/mcp/${businessSlug}`,
|
||||
protocol: 'streamable-http',
|
||||
version: '0.1.0',
|
||||
business: businessSlug,
|
||||
provider: 'geolocal.io',
|
||||
discovery: {
|
||||
transport: 'stdio',
|
||||
tools: [
|
||||
'get_business_info',
|
||||
'get_hours',
|
||||
'get_services',
|
||||
'get_booking_link',
|
||||
'get_related_businesses',
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function getManifestForBusiness(
|
||||
businessSlug: string,
|
||||
serverBaseUrl: string = 'https://mcp.geolocal.io'
|
||||
): Promise<Record<string, any>> {
|
||||
const result = await pool.query(
|
||||
`SELECT slug, name, mcp_endpoint FROM businesses WHERE slug = $1`,
|
||||
[businessSlug]
|
||||
);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
throw new Error(`Business '${businessSlug}' not found`);
|
||||
}
|
||||
|
||||
const b = result.rows[0];
|
||||
const baseUrl = b.mcp_endpoint || serverBaseUrl;
|
||||
return generateManifest({ businessSlug, serverBaseUrl: baseUrl });
|
||||
}
|
||||
|
||||
/**
|
||||
* CLI: Generate a manifest file for a given business slug.
|
||||
* Usage: npx tsx src/manifest-generator.ts --slug=frisco-german-auto
|
||||
*/
|
||||
async function cli() {
|
||||
const args = process.argv.slice(2);
|
||||
const slugArg = args.find((a) => a.startsWith('--slug='));
|
||||
if (!slugArg) {
|
||||
console.error('Usage: npx tsx src/manifest-generator.ts --slug=<business-slug>');
|
||||
process.exit(1);
|
||||
}
|
||||
const slug = slugArg.split('=')[1];
|
||||
const manifest = await getManifestForBusiness(slug);
|
||||
console.log(JSON.stringify(manifest, null, 2));
|
||||
}
|
||||
|
||||
if (process.argv[1]?.includes('manifest-generator')) {
|
||||
cli().catch(console.error);
|
||||
}
|
||||
Reference in New Issue
Block a user