@kya-os/mcp-i
Version:
The TypeScript MCP framework with identity features built-in
245 lines (244 loc) • 8.26 kB
JavaScript
;
/**
* Well-Known Endpoints for XMCP-I Runtime
*
* Handles /.well-known/did.json and /.well-known/agent.json endpoints
* according to requirements 7.1, 7.2, 7.3, 7.4, 7.5.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.WellKnownManager = void 0;
exports.createWellKnownHandler = createWellKnownHandler;
exports.validateDIDDocument = validateDIDDocument;
exports.validateAgentDocument = validateAgentDocument;
exports.extractDIDFromPath = extractDIDFromPath;
// Load base-x synchronously using require (mcp-i is CommonJS)
// eslint-disable-next-line @typescript-eslint/no-var-requires
const baseX = require("base-x");
const base58 = baseX.default || baseX;
const base58Encoder = base58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz');
/**
* Well-known endpoints manager
*/
class WellKnownManager {
identity;
config;
constructor(identity, config) {
this.identity = identity;
this.config = config;
}
/**
* Generate DID document for /.well-known/did.json
* Requirements: 7.1, 7.5
*/
generateDIDDocument() {
const kid = `#${this.identity.kid}`;
// Convert base64 public key to multibase format
const publicKeyMultibase = this.encodePublicKeyMultibase(this.identity.publicKey);
const didDocument = {
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/ed25519-2020/v1",
],
id: this.identity.did,
verificationMethod: [
{
id: kid,
type: "Ed25519VerificationKey2020",
controller: this.identity.did,
publicKeyMultibase,
},
],
authentication: [kid],
assertionMethod: [kid],
};
return didDocument;
}
/**
* Generate agent document for /.well-known/agent.json
* Requirements: 7.2, 7.3
*/
generateAgentDocument() {
const agentDocument = {
id: this.identity.did,
capabilities: {
"mcp-i": ["handshake", "signing", "verification"], // Exact capability triplet
},
};
// Add registry URLs if configured
if (this.config.registryUrls) {
agentDocument.registry = { ...this.config.registryUrls };
}
// Add metadata if configured
if (this.config.agentMetadata) {
agentDocument.metadata = { ...this.config.agentMetadata };
}
return agentDocument;
}
/**
* Get HTTP headers for DID document
* Requirements: 7.4, 7.5
*/
getDIDDocumentHeaders() {
const headers = {
"Content-Type": "application/did+json",
};
// Add caching headers based on environment
if (this.config.environment === "production") {
headers["Cache-Control"] = "public, max-age=300"; // 5 minutes
}
else {
headers["Cache-Control"] = "no-store";
}
return headers;
}
/**
* Get HTTP headers for agent document
* Requirements: 7.2, 7.5
*/
getAgentDocumentHeaders() {
const headers = {
"Content-Type": "application/json",
};
// Always no-store for agent document in dev, public cache in prod
if (this.config.environment === "production") {
headers["Cache-Control"] = "public, max-age=300"; // 5 minutes
}
else {
headers["Cache-Control"] = "no-store";
}
return headers;
}
/**
* Encode public key as multibase (base58btc with 'z' prefix for Ed25519)
*/
encodePublicKeyMultibase(base64PublicKey) {
// For Ed25519, we use base58btc encoding with 'z' prefix
const publicKeyBytes = Buffer.from(base64PublicKey, "base64");
// Ed25519 public key prefix (0xed01) + key bytes
const prefixedKey = Buffer.concat([
Buffer.from([0xed, 0x01]), // Ed25519 multicodec prefix
publicKeyBytes,
]);
// Convert to base58btc using base-x library
const base58Encoded = base58Encoder.encode(prefixedKey);
return `z${base58Encoded}`; // 'z' prefix indicates base58btc
}
/**
* Update configuration
*/
updateConfig(config) {
this.config = { ...this.config, ...config };
}
/**
* Get current configuration
*/
getConfig() {
return { ...this.config };
}
}
exports.WellKnownManager = WellKnownManager;
/**
* Create well-known endpoint handler
*/
function createWellKnownHandler(identity, config) {
const manager = new WellKnownManager(identity, config);
return {
handleDIDDocument() {
try {
const didDocument = manager.generateDIDDocument();
const headers = manager.getDIDDocumentHeaders();
return {
status: 200,
headers,
body: JSON.stringify(didDocument, null, 2),
};
}
catch (error) {
return {
status: 500,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
error: "Failed to generate DID document",
message: error instanceof Error ? error.message : "Unknown error",
}),
};
}
},
handleAgentDocument() {
try {
const agentDocument = manager.generateAgentDocument();
const headers = manager.getAgentDocumentHeaders();
return {
status: 200,
headers,
body: JSON.stringify(agentDocument, null, 2),
};
}
catch (error) {
return {
status: 500,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
error: "Failed to generate agent document",
message: error instanceof Error ? error.message : "Unknown error",
}),
};
}
},
};
}
/**
* Utility functions
*/
/**
* Validate DID document structure
*/
function validateDIDDocument(doc) {
return (typeof doc === "object" &&
doc !== null &&
Array.isArray(doc["@context"]) &&
typeof doc.id === "string" &&
doc.id.startsWith("did:") &&
Array.isArray(doc.verificationMethod) &&
Array.isArray(doc.authentication) &&
Array.isArray(doc.assertionMethod));
}
/**
* Validate agent document structure
*/
function validateAgentDocument(doc) {
return (typeof doc === "object" &&
doc !== null &&
typeof doc.id === "string" &&
doc.id.startsWith("did:") &&
typeof doc.capabilities === "object" &&
Array.isArray(doc.capabilities["mcp-i"]) &&
doc.capabilities["mcp-i"].length === 3 &&
doc.capabilities["mcp-i"].includes("handshake") &&
doc.capabilities["mcp-i"].includes("signing") &&
doc.capabilities["mcp-i"].includes("verification"));
}
/**
* Extract DID from URL path (for did:web resolution)
*/
function extractDIDFromPath(path, baseUrl) {
try {
// For did:web, the DID is constructed from the domain and path
// Example: /.well-known/did.json -> did:web:example.com
// Example: /agents/my-agent/.well-known/did.json -> did:web:example.com:agents:my-agent
const url = new URL(baseUrl);
const domain = url.hostname;
// Remove /.well-known/did.json from path
const cleanPath = path.replace("/.well-known/did.json", "");
if (cleanPath === "") {
return `did:web:${domain}`;
}
// Convert path segments to DID path components
const pathComponents = cleanPath.split("/").filter(Boolean);
const didPath = pathComponents.join(":");
return `did:web:${domain}:${didPath}`;
}
catch {
return null;
}
}