@takuhon/core 0.13.0 → 0.14.0

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/dist/index.d.ts CHANGED
@@ -3801,6 +3801,115 @@ interface CvDocument {
3801
3801
  */
3802
3802
  declare function deriveCv(localized: LocalizedTakuhon): CvDocument;
3803
3803
 
3804
+ /**
3805
+ * MCP (Model Context Protocol) projection of a profile.
3806
+ *
3807
+ * This module describes how a takuhon profile is exposed to an AI agent as a
3808
+ * set of read-only MCP tools and resources, and how each one is answered from a
3809
+ * profile document — *without* committing to a transport (stdio / HTTP) or to
3810
+ * any MCP SDK.
3811
+ *
3812
+ * The split mirrors the rest of core: deciding *what* to expose and *what it
3813
+ * returns* is a pure, deterministic projection over the canonical document
3814
+ * (exactly like {@link generateJsonLd}), so it lives here in `@takuhon/core` and
3815
+ * stays runtime-free (spec §2.3). The transport, JSON-RPC framing, and server
3816
+ * lifecycle are runtime-aware; they live in `@takuhon/mcp` and the CLI /
3817
+ * Cloudflare adapters, which call the pure executors below.
3818
+ *
3819
+ * Everything here is read-only. No tool or resource touches the admin surface,
3820
+ * and every answer passes through {@link applyPublicPrivacyFilter}, so an MCP
3821
+ * client sees exactly what `GET /api/profile`, `GET /api/jsonld`, `GET
3822
+ * /api/schema`, and `GET /takuhon.json` already expose — no more.
3823
+ *
3824
+ * The catalog ({@link MCP_TOOLS} / {@link MCP_RESOURCES}) is plain data with no
3825
+ * SDK dependency, so the `@takuhon/mcp` wiring can register it against the
3826
+ * official `@modelcontextprotocol/sdk` and later be swapped to a hand-rolled
3827
+ * dispatcher without touching this file.
3828
+ */
3829
+
3830
+ /**
3831
+ * Profile sections a `get_section` call may request: the content-bearing keys
3832
+ * of a locale-resolved profile (the spec §6 data sections). `settings`,
3833
+ * `meta`, `schemaVersion`, and `resolvedLocale` are intentionally excluded —
3834
+ * they are configuration / bookkeeping, not profile content an agent reads.
3835
+ */
3836
+ declare const MCP_PROFILE_SECTIONS: readonly ["profile", "links", "careers", "projects", "skills", "certifications", "memberships", "volunteering", "honors", "education", "publications", "languages", "courses", "patents", "testScores", "recommendations", "contact"];
3837
+ /** One of the {@link MCP_PROFILE_SECTIONS} a `get_section` call accepts. */
3838
+ type McpProfileSection = (typeof MCP_PROFILE_SECTIONS)[number];
3839
+ /** A JSON Schema (draft 2020-12) object describing a tool's input arguments. */
3840
+ type McpInputSchema = Readonly<Record<string, unknown>>;
3841
+ /** A read-only MCP tool definition — plain data, no SDK dependency. */
3842
+ interface McpToolDefinition {
3843
+ /** Stable tool id used in `tools/call`. */
3844
+ name: string;
3845
+ /** Human-readable title for display. */
3846
+ title: string;
3847
+ /** What the tool returns; shown to the agent when it chooses a tool. */
3848
+ description: string;
3849
+ /** JSON Schema for the tool's `arguments` object. */
3850
+ inputSchema: McpInputSchema;
3851
+ }
3852
+ /** A read-only MCP resource definition — plain data, no SDK dependency. */
3853
+ interface McpResourceDefinition {
3854
+ /** Stable resource URI used in `resources/read`. */
3855
+ uri: string;
3856
+ name: string;
3857
+ title: string;
3858
+ description: string;
3859
+ /** MIME type of the resource contents. */
3860
+ mimeType: string;
3861
+ }
3862
+ /** Result of {@link executeMcpTool}: the structured value the tool produced. */
3863
+ interface McpToolResult {
3864
+ /**
3865
+ * The JSON value the tool produced — already normalized, locale-resolved, and
3866
+ * privacy-filtered. The transport layer serializes this to MCP wire content.
3867
+ */
3868
+ data: unknown;
3869
+ /** Locale the result was resolved at, when the tool resolved one. */
3870
+ resolvedLocale?: string;
3871
+ }
3872
+ /** Result of {@link readMcpResource}: one resource's contents. */
3873
+ interface McpResourceResult {
3874
+ uri: string;
3875
+ mimeType: string;
3876
+ /** The JSON value of the resource. The transport layer serializes it. */
3877
+ data: unknown;
3878
+ }
3879
+ /**
3880
+ * Thrown by {@link executeMcpTool} / {@link readMcpResource} for an unknown
3881
+ * tool/resource name or invalid arguments. The transport layer maps it to the
3882
+ * appropriate MCP error (a JSON-RPC error for a malformed request, or an
3883
+ * `isError` tool result for a bad argument) — core stays transport-agnostic.
3884
+ */
3885
+ declare class McpRequestError extends Error {
3886
+ constructor(message: string, options?: {
3887
+ cause?: unknown;
3888
+ });
3889
+ }
3890
+ /** The read-only tools an MCP client may call against the profile. */
3891
+ declare const MCP_TOOLS: readonly McpToolDefinition[];
3892
+ /** The read-only resources an MCP client may read. */
3893
+ declare const MCP_RESOURCES: readonly McpResourceDefinition[];
3894
+ /**
3895
+ * Execute a read-only MCP tool against a profile document.
3896
+ *
3897
+ * `profile` is the raw validated {@link Takuhon} (the transport layer loaded it
3898
+ * from KV / a file). Locale-aware tools normalize, locale-resolve, and
3899
+ * privacy-filter it exactly as `GET /api/profile` does, then project the
3900
+ * requested view. Pure and deterministic — no clock, no randomness, no I/O.
3901
+ *
3902
+ * @throws {McpRequestError} for an unknown tool name or invalid arguments.
3903
+ */
3904
+ declare function executeMcpTool(name: string, args: Readonly<Record<string, unknown>>, profile: Takuhon): McpToolResult;
3905
+ /**
3906
+ * Read a read-only MCP resource from a profile document. Pure and
3907
+ * deterministic.
3908
+ *
3909
+ * @throws {McpRequestError} for an unknown resource URI.
3910
+ */
3911
+ declare function readMcpResource(uri: string, profile: Takuhon): McpResourceResult;
3912
+
3804
3913
  /**
3805
3914
  * Image-asset helpers for uploaded media (avatars, project images): magic-byte
3806
3915
  * type detection, header-only dimension / frame reading, and metadata stripping
@@ -3901,4 +4010,4 @@ declare function stripImageMetadata(bytes: Uint8Array, mime: AcceptedImageMime):
3901
4010
  */
3902
4011
  declare const SCHEMA_VERSION = "0.5.0";
3903
4012
 
3904
- export { ACCEPTED_IMAGE_MIME_TYPES, type AcceptedImageMime, type ActivitySettings, type ActivitySnapshot, type ActivityStorage, type Address, type AssetOptions, type AssetRecord, type Avatar, type Career, type Certification, type CodingTime, ConflictError, type Contact, type ContentLicense, type ContentLicenseAttribution, type ContributionCalendar, type ContributionDay, type Course, type CvDocument, type CvHeader, type CvSection, type CvSectionKind, type Education, type ExportOptions, type ExportedTakuhon, type Honor, IMAGE_EXTENSIONS, type ImageInfo, ImportError, type Iso3166Alpha2, type IsoDateTime, type Language, type LanguageBreakdown, type LanguageProficiency, type Link, type LinkBuiltin, type LinkCustom, type LinkType, type LocaleTag, type LocalizedAddress, type LocalizedAvatar, type LocalizedBody, type LocalizedCareer, type LocalizedCertification, type LocalizedCourse, type LocalizedEducation, type LocalizedHonor, type LocalizedLanguage, type LocalizedLink, type LocalizedLinkBuiltin, type LocalizedLinkCustom, type LocalizedMembership, type LocalizedPatent, type LocalizedProfile, type LocalizedProject, type LocalizedPublication, type LocalizedRecommendation, type LocalizedRecommendationAuthor, type LocalizedTakuhon, type LocalizedTestScore, type LocalizedTitle, type LocalizedVolunteering, MAX_IMAGE_BYTES, MAX_IMAGE_DIMENSION, MAX_IMAGE_FRAMES, type Membership, type Meta, type MetaPrivacy, type Migration, MigrationError, type NormalizedTakuhon, NotFoundError, type Patent, type PatentStatus, type Profile, type Project, type Publication, RANK_FULL_CODING_HOURS, RANK_FULL_CONTRIBUTIONS, RANK_TIER_THRESHOLDS, type RankInput, type RankTier, type RankTierLabel, type Recommendation, type RecommendationAuthor, SCHEMA_VERSION, SUPPORTED_SCHEMA_VERSIONS, type Schema, type Settings, type Skill, type Slug, StorageError, type Takuhon, type TakuhonAssetStorage, type TakuhonStorage, type TestScore, type ValidationError, type ValidationResult, type Volunteering, type YearMonth, applyPublicPrivacyFilter, computeLanguagePercentages, deriveCv, deriveRankTier, detectImageMime, exportTakuhon, formatCodingTime, generateJsonLd, generatePersonJsonLd, generateProfilePageJsonLd, importTakuhon, isActivitySnapshot, migrateTakuhon, migrations, normalize, readImageInfo, renderActivitySvg, resolveLocale, schema, stripImageMetadata, validate };
4013
+ export { ACCEPTED_IMAGE_MIME_TYPES, type AcceptedImageMime, type ActivitySettings, type ActivitySnapshot, type ActivityStorage, type Address, type AssetOptions, type AssetRecord, type Avatar, type Career, type Certification, type CodingTime, ConflictError, type Contact, type ContentLicense, type ContentLicenseAttribution, type ContributionCalendar, type ContributionDay, type Course, type CvDocument, type CvHeader, type CvSection, type CvSectionKind, type Education, type ExportOptions, type ExportedTakuhon, type Honor, IMAGE_EXTENSIONS, type ImageInfo, ImportError, type Iso3166Alpha2, type IsoDateTime, type Language, type LanguageBreakdown, type LanguageProficiency, type Link, type LinkBuiltin, type LinkCustom, type LinkType, type LocaleTag, type LocalizedAddress, type LocalizedAvatar, type LocalizedBody, type LocalizedCareer, type LocalizedCertification, type LocalizedCourse, type LocalizedEducation, type LocalizedHonor, type LocalizedLanguage, type LocalizedLink, type LocalizedLinkBuiltin, type LocalizedLinkCustom, type LocalizedMembership, type LocalizedPatent, type LocalizedProfile, type LocalizedProject, type LocalizedPublication, type LocalizedRecommendation, type LocalizedRecommendationAuthor, type LocalizedTakuhon, type LocalizedTestScore, type LocalizedTitle, type LocalizedVolunteering, MAX_IMAGE_BYTES, MAX_IMAGE_DIMENSION, MAX_IMAGE_FRAMES, MCP_PROFILE_SECTIONS, MCP_RESOURCES, MCP_TOOLS, type McpInputSchema, type McpProfileSection, McpRequestError, type McpResourceDefinition, type McpResourceResult, type McpToolDefinition, type McpToolResult, type Membership, type Meta, type MetaPrivacy, type Migration, MigrationError, type NormalizedTakuhon, NotFoundError, type Patent, type PatentStatus, type Profile, type Project, type Publication, RANK_FULL_CODING_HOURS, RANK_FULL_CONTRIBUTIONS, RANK_TIER_THRESHOLDS, type RankInput, type RankTier, type RankTierLabel, type Recommendation, type RecommendationAuthor, SCHEMA_VERSION, SUPPORTED_SCHEMA_VERSIONS, type Schema, type Settings, type Skill, type Slug, StorageError, type Takuhon, type TakuhonAssetStorage, type TakuhonStorage, type TestScore, type ValidationError, type ValidationResult, type Volunteering, type YearMonth, applyPublicPrivacyFilter, computeLanguagePercentages, deriveCv, deriveRankTier, detectImageMime, executeMcpTool, exportTakuhon, formatCodingTime, generateJsonLd, generatePersonJsonLd, generateProfilePageJsonLd, importTakuhon, isActivitySnapshot, migrateTakuhon, migrations, normalize, readImageInfo, readMcpResource, renderActivitySvg, resolveLocale, schema, stripImageMetadata, validate };
package/dist/index.js CHANGED
@@ -11587,6 +11587,161 @@ function deriveCv(localized) {
11587
11587
  };
11588
11588
  }
11589
11589
 
11590
+ // src/mcp.ts
11591
+ var MCP_PROFILE_SECTIONS = [
11592
+ "profile",
11593
+ "links",
11594
+ "careers",
11595
+ "projects",
11596
+ "skills",
11597
+ "certifications",
11598
+ "memberships",
11599
+ "volunteering",
11600
+ "honors",
11601
+ "education",
11602
+ "publications",
11603
+ "languages",
11604
+ "courses",
11605
+ "patents",
11606
+ "testScores",
11607
+ "recommendations",
11608
+ "contact"
11609
+ ];
11610
+ var McpRequestError = class extends Error {
11611
+ constructor(message, options) {
11612
+ super(message, options);
11613
+ this.name = "McpRequestError";
11614
+ }
11615
+ };
11616
+ var LANG_PROPERTY = {
11617
+ type: "string",
11618
+ description: 'BCP-47 locale tag (e.g. "ja", "en"). Resolves to the requested locale, falling back to the profile default when omitted or unavailable.'
11619
+ };
11620
+ var MCP_TOOLS = [
11621
+ {
11622
+ name: "get_profile",
11623
+ title: "Get profile",
11624
+ description: "Return the owner's full public profile, resolved to one locale and privacy-filtered (identical to the public `GET /api/profile`).",
11625
+ inputSchema: {
11626
+ type: "object",
11627
+ properties: { lang: LANG_PROPERTY },
11628
+ additionalProperties: false
11629
+ }
11630
+ },
11631
+ {
11632
+ name: "get_section",
11633
+ title: "Get profile section",
11634
+ description: "Return a single section of the public profile (e.g. careers, education, skills), resolved to one locale and privacy-filtered.",
11635
+ inputSchema: {
11636
+ type: "object",
11637
+ properties: {
11638
+ section: {
11639
+ type: "string",
11640
+ enum: [...MCP_PROFILE_SECTIONS],
11641
+ description: "Which profile section to return."
11642
+ },
11643
+ lang: LANG_PROPERTY
11644
+ },
11645
+ required: ["section"],
11646
+ additionalProperties: false
11647
+ }
11648
+ },
11649
+ {
11650
+ name: "get_jsonld",
11651
+ title: "Get JSON-LD",
11652
+ description: "Return the Schema.org JSON-LD (a `ProfilePage` wrapping a `Person`) for the profile, resolved to one locale (identical to `GET /api/jsonld`).",
11653
+ inputSchema: {
11654
+ type: "object",
11655
+ properties: { lang: LANG_PROPERTY },
11656
+ additionalProperties: false
11657
+ }
11658
+ },
11659
+ {
11660
+ name: "list_locales",
11661
+ title: "List locales",
11662
+ description: "List the locales this profile is available in, plus the default \u2014 useful before calling another tool with a `lang` argument.",
11663
+ inputSchema: { type: "object", properties: {}, additionalProperties: false }
11664
+ }
11665
+ ];
11666
+ var MCP_RESOURCES = [
11667
+ {
11668
+ uri: "takuhon://profile",
11669
+ name: "profile",
11670
+ title: "Canonical profile (takuhon.json)",
11671
+ description: "The full canonical takuhon.json, privacy-filtered, with every locale retained (identical to `GET /takuhon.json`).",
11672
+ mimeType: "application/json"
11673
+ },
11674
+ {
11675
+ uri: "takuhon://schema",
11676
+ name: "schema",
11677
+ title: "Takuhon JSON Schema",
11678
+ description: "The public JSON Schema contract the profile conforms to (identical to `GET /api/schema`). Lets an agent understand the document shape.",
11679
+ mimeType: "application/json"
11680
+ }
11681
+ ];
11682
+ function executeMcpTool(name, args, profile) {
11683
+ switch (name) {
11684
+ case "get_profile": {
11685
+ const localized = publicView(profile, readLang(args));
11686
+ return { data: localized, resolvedLocale: localized.resolvedLocale };
11687
+ }
11688
+ case "get_section": {
11689
+ const section = readSection(args);
11690
+ const localized = publicView(profile, readLang(args));
11691
+ return { data: localized[section], resolvedLocale: localized.resolvedLocale };
11692
+ }
11693
+ case "get_jsonld": {
11694
+ const localized = publicView(profile, readLang(args));
11695
+ return { data: generateJsonLd(localized), resolvedLocale: localized.resolvedLocale };
11696
+ }
11697
+ case "list_locales":
11698
+ return {
11699
+ data: {
11700
+ defaultLocale: profile.settings.defaultLocale,
11701
+ availableLocales: profile.settings.availableLocales,
11702
+ ...profile.settings.fallbackLocale !== void 0 ? { fallbackLocale: profile.settings.fallbackLocale } : {}
11703
+ }
11704
+ };
11705
+ default:
11706
+ throw new McpRequestError(`Unknown MCP tool: ${name}`);
11707
+ }
11708
+ }
11709
+ function readMcpResource(uri, profile) {
11710
+ switch (uri) {
11711
+ case "takuhon://profile":
11712
+ return {
11713
+ uri,
11714
+ mimeType: "application/json",
11715
+ data: applyPublicPrivacyFilter(profile)
11716
+ };
11717
+ case "takuhon://schema":
11718
+ return { uri, mimeType: "application/json", data: schema };
11719
+ default:
11720
+ throw new McpRequestError(`Unknown MCP resource: ${uri}`);
11721
+ }
11722
+ }
11723
+ function publicView(profile, lang) {
11724
+ return applyPublicPrivacyFilter(resolveLocale(normalize(profile), lang));
11725
+ }
11726
+ function readLang(args) {
11727
+ const lang = args.lang;
11728
+ if (lang === void 0) return void 0;
11729
+ if (typeof lang !== "string") {
11730
+ throw new McpRequestError("`lang` must be a string (a BCP-47 locale tag).");
11731
+ }
11732
+ return lang;
11733
+ }
11734
+ function readSection(args) {
11735
+ const section = args.section;
11736
+ if (typeof section !== "string" || !isProfileSection(section)) {
11737
+ throw new McpRequestError(`\`section\` must be one of: ${MCP_PROFILE_SECTIONS.join(", ")}.`);
11738
+ }
11739
+ return section;
11740
+ }
11741
+ function isProfileSection(value) {
11742
+ return MCP_PROFILE_SECTIONS.includes(value);
11743
+ }
11744
+
11590
11745
  // src/image.ts
11591
11746
  var ACCEPTED_IMAGE_MIME_TYPES = [
11592
11747
  "image/jpeg",
@@ -11927,6 +12082,10 @@ export {
11927
12082
  MAX_IMAGE_BYTES,
11928
12083
  MAX_IMAGE_DIMENSION,
11929
12084
  MAX_IMAGE_FRAMES,
12085
+ MCP_PROFILE_SECTIONS,
12086
+ MCP_RESOURCES,
12087
+ MCP_TOOLS,
12088
+ McpRequestError,
11930
12089
  MigrationError,
11931
12090
  NotFoundError,
11932
12091
  RANK_FULL_CODING_HOURS,
@@ -11940,6 +12099,7 @@ export {
11940
12099
  deriveCv,
11941
12100
  deriveRankTier,
11942
12101
  detectImageMime,
12102
+ executeMcpTool,
11943
12103
  exportTakuhon,
11944
12104
  formatCodingTime,
11945
12105
  generateJsonLd,
@@ -11951,6 +12111,7 @@ export {
11951
12111
  migrations,
11952
12112
  normalize,
11953
12113
  readImageInfo,
12114
+ readMcpResource,
11954
12115
  renderActivitySvg,
11955
12116
  resolveLocale,
11956
12117
  schema,