[{"data":1,"prerenderedAt":1364},["ShallowReactive",2],{"navigation_docs":3,"-platform-mcp-tools":213,"-platform-mcp-tools-surround":1359},[4,142],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":36},"Kinotic Apps","i-lucide-rocket","\u002Fapps","01.apps",[10,14,18,37,58,91,102,122,127],{"title":11,"path":12,"stem":13},"Introduction","\u002Fapps\u002Fintroduction","01.apps\u002F01.introduction",{"title":15,"path":16,"stem":17},"Quick Start","\u002Fapps\u002Fquick-start","01.apps\u002F02.quick-start",{"title":19,"icon":20,"path":21,"stem":22,"children":23,"page":36},"Application Structure","i-lucide-folder-tree","\u002Fapps\u002Fapplication-structure","01.apps\u002F03.application-structure",[24,28,32],{"title":25,"path":26,"stem":27},"Overview","\u002Fapps\u002Fapplication-structure\u002Foverview","01.apps\u002F03.application-structure\u002F01.overview",{"title":29,"path":30,"stem":31},"Applications and Projects","\u002Fapps\u002Fapplication-structure\u002Fapplications-and-projects","01.apps\u002F03.application-structure\u002F02.applications-and-projects",{"title":33,"path":34,"stem":35},"Artifact Types","\u002Fapps\u002Fapplication-structure\u002Fartifact-types","01.apps\u002F03.application-structure\u002F03.artifact-types",false,{"title":38,"icon":39,"path":40,"stem":41,"children":42,"page":36},"Services","i-lucide-network","\u002Fapps\u002Fservices","01.apps\u002F04.services",[43,46,50,54],{"title":25,"path":44,"stem":45},"\u002Fapps\u002Fservices\u002Foverview","01.apps\u002F04.services\u002F01.overview",{"title":47,"path":48,"stem":49},"Publishing Services","\u002Fapps\u002Fservices\u002Fpublishing-services","01.apps\u002F04.services\u002F02.publishing-services",{"title":51,"path":52,"stem":53},"Service Proxies","\u002Fapps\u002Fservices\u002Fservice-proxies","01.apps\u002F04.services\u002F03.service-proxies",{"title":55,"path":56,"stem":57},"Streaming","\u002Fapps\u002Fservices\u002Fstreaming","01.apps\u002F04.services\u002F04.streaming",{"title":59,"icon":60,"path":61,"stem":62,"children":63,"page":36},"Persistence","i-lucide-database","\u002Fapps\u002Fpersistence","01.apps\u002F05.persistence",[64,67,71,75,79,83,87],{"title":25,"path":65,"stem":66},"\u002Fapps\u002Fpersistence\u002Foverview","01.apps\u002F05.persistence\u002F01.overview",{"title":68,"path":69,"stem":70},"Defining Entities","\u002Fapps\u002Fpersistence\u002Fdefining-entities","01.apps\u002F05.persistence\u002F02.defining-entities",{"title":72,"path":73,"stem":74},"Entity Decorators","\u002Fapps\u002Fpersistence\u002Fentity-decorators","01.apps\u002F05.persistence\u002F03.entity-decorators",{"title":76,"path":77,"stem":78},"CRUD Operations","\u002Fapps\u002Fpersistence\u002Fcrud-operations","01.apps\u002F05.persistence\u002F04.crud-operations",{"title":80,"path":81,"stem":82},"Named Queries","\u002Fapps\u002Fpersistence\u002Fnamed-queries","01.apps\u002F05.persistence\u002F05.named-queries",{"title":84,"path":85,"stem":86},"Multi-Tenancy","\u002Fapps\u002Fpersistence\u002Fmulti-tenancy","01.apps\u002F05.persistence\u002F06.multi-tenancy",{"title":88,"path":89,"stem":90},"Migrations","\u002Fapps\u002Fpersistence\u002Fmigrations","01.apps\u002F05.persistence\u002F07.migrations",{"title":92,"icon":93,"path":94,"stem":95,"children":96,"page":36},"Security","i-lucide-shield-check","\u002Fapps\u002Fsecurity","01.apps\u002F06.security",[97],{"title":98,"path":99,"stem":100,"icon":101},"Authentication","\u002Fapps\u002Fsecurity\u002Fauthentication","01.apps\u002F06.security\u002F01.authentication","i-lucide-key-round",{"title":103,"icon":104,"path":105,"stem":106,"children":107,"page":36},"Deployment","i-lucide-cloud-upload","\u002Fapps\u002Fdeployment","01.apps\u002F07.deployment",[108,113,118],{"title":109,"path":110,"stem":111,"icon":112},"Deployment Workflow","\u002Fapps\u002Fdeployment\u002Fworkflow","01.apps\u002F07.deployment\u002F01.workflow","i-lucide-git-branch",{"title":114,"path":115,"stem":116,"icon":117},"Environments","\u002Fapps\u002Fdeployment\u002Fenvironments","01.apps\u002F07.deployment\u002F02.environments","i-lucide-server",{"title":119,"path":120,"stem":121,"icon":6},"Push to Deploy","\u002Fapps\u002Fdeployment\u002Fpush-to-deploy","01.apps\u002F07.deployment\u002F03.push-to-deploy",{"title":123,"path":124,"stem":125,"icon":126},"CLI Reference","\u002Fapps\u002Fcli-reference","01.apps\u002F08.cli-reference","i-lucide-terminal",{"title":128,"icon":129,"path":130,"stem":131,"children":132,"page":36},"Reference","i-lucide-book-open","\u002Fapps\u002Freference","01.apps\u002F09.reference",[133,138],{"title":134,"path":135,"stem":136,"icon":137},"Decorators Reference","\u002Fapps\u002Freference\u002Fdecorators","01.apps\u002F09.reference\u002F01.decorators","i-lucide-at-sign",{"title":139,"path":140,"stem":141,"icon":60},"Migration SQL Grammar","\u002Fapps\u002Freference\u002Fmigration-sql-grammar","01.apps\u002F09.reference\u002F02.migration-sql-grammar",{"title":143,"icon":117,"path":144,"stem":145,"children":146,"page":36},"Kinotic OS","\u002Fplatform","02.platform",[147,152,156,161,166,171,175,180,185,190,195],{"title":148,"path":149,"stem":150,"icon":151},"System Architecture","\u002Fplatform\u002Farchitecture","02.platform\u002F01.architecture","i-lucide-boxes",{"title":153,"path":154,"stem":155,"icon":6},"Deployment Guide","\u002Fplatform\u002Fdeployment-guide","02.platform\u002F02.deployment-guide",{"title":157,"path":158,"stem":159,"icon":160},"Configuration","\u002Fplatform\u002Fconfiguration","02.platform\u002F03.configuration","i-lucide-settings",{"title":162,"path":163,"stem":164,"icon":165},"Organization Management","\u002Fplatform\u002Forganization-management","02.platform\u002F04.organization-management","i-lucide-building",{"title":167,"path":168,"stem":169,"icon":170},"System Security","\u002Fplatform\u002Fsystem-security","02.platform\u002F05.system-security","i-lucide-shield",{"title":172,"path":173,"stem":174,"icon":93},"Defense in Depth","\u002Fplatform\u002Fdefense-in-depth","02.platform\u002F06.defense-in-depth",{"title":176,"path":177,"stem":178,"icon":179},"MCP Tools","\u002Fplatform\u002Fmcp-tools","02.platform\u002F07.mcp-tools","i-lucide-bot",{"title":181,"path":182,"stem":183,"icon":184},"Observability","\u002Fplatform\u002Fobservability","02.platform\u002F08.observability","i-lucide-activity",{"title":186,"path":187,"stem":188,"icon":189},"Contributing","\u002Fplatform\u002Fcontributing","02.platform\u002F09.contributing","i-lucide-git-pull-request",{"title":191,"path":192,"stem":193,"icon":194},"System Migrations","\u002Fplatform\u002Fsystem-migrations","02.platform\u002F10.system-migrations","i-lucide-database-zap",{"title":128,"icon":129,"path":196,"stem":197,"children":198,"page":36},"\u002Fplatform\u002Freference","02.platform\u002F11.reference",[199,204,209],{"title":200,"path":201,"stem":202,"icon":203},"CRI Format","\u002Fplatform\u002Freference\u002Fcri-format","02.platform\u002F11.reference\u002F01.cri-format","i-lucide-link",{"title":205,"path":206,"stem":207,"icon":208},"Grind Jobs","\u002Fplatform\u002Freference\u002Fgrind-jobs","02.platform\u002F11.reference\u002F02.grind-jobs","i-lucide-workflow",{"title":210,"path":211,"stem":212,"icon":151},"Project Publishing Design","\u002Fplatform\u002Freference\u002Fproject-publishing-design","02.platform\u002F11.reference\u002F03.project-publishing-design",{"id":214,"title":176,"body":215,"description":1352,"extension":1353,"links":1354,"meta":1355,"navigation":1356,"path":177,"seo":1357,"stem":178,"__hash__":1358},"docs\u002F02.platform\u002F07.mcp-tools.md",{"type":216,"value":217,"toc":1342},"minimark",[218,222,247,250,254,308,314,351,364,420,428,442,453,461,493,497,515,558,574,577,587,601,607,626,630,646,667,670,885,891,904,921,936,942,962,966,969,975,978,987,993,1002,1009,1013,1039,1043,1046,1089,1111,1215,1237,1260,1284,1322,1338],[219,220,25],"h2",{"id":221},"overview",[223,224,225,226,233,234,238,239,242,243,246],"p",{},"The API gateway serves published service functions as ",[227,228,232],"a",{"href":229,"rel":230},"https:\u002F\u002Fmodelcontextprotocol.io",[231],"nofollow","MCP (Model Context Protocol)"," tools, so an LLM can call them through ",[235,236,237],"code",{},"POST \u002Fmcp",". A function opts in with the ",[235,240,241],{},"@McpTool"," annotation on a ",[235,244,245],{},"@Publish","ed interface's method; the gateway serves each caller only the tools their participant may reach.",[223,248,249],{},"Tool authoring is currently a platform capability: the annotated services are those hosted by Kinotic OS itself. Application-level tool authoring is not yet implemented and will be documented with the apps docs when it lands.",[219,251,253],{"id":252},"exposing-a-function","Exposing a function",[255,256,261],"pre",{"className":257,"code":258,"language":259,"meta":260,"style":260},"language-java shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","@Publish\n@Version(\"1.0.0\")\npublic interface OrderService {\n\n    @McpTool(title = \"Find Order\", description = \"Finds an order by its id\", readOnlyHint = true, idempotentHint = true)\n    CompletableFuture\u003COrder> findById(String orderId);\n}\n","java","",[235,262,263,271,277,283,290,296,302],{"__ignoreMap":260},[264,265,268],"span",{"class":266,"line":267},"line",1,[264,269,270],{},"@Publish\n",[264,272,274],{"class":266,"line":273},2,[264,275,276],{},"@Version(\"1.0.0\")\n",[264,278,280],{"class":266,"line":279},3,[264,281,282],{},"public interface OrderService {\n",[264,284,286],{"class":266,"line":285},4,[264,287,289],{"emptyLinePlaceholder":288},true,"\n",[264,291,293],{"class":266,"line":292},5,[264,294,295],{},"    @McpTool(title = \"Find Order\", description = \"Finds an order by its id\", readOnlyHint = true, idempotentHint = true)\n",[264,297,299],{"class":266,"line":298},6,[264,300,301],{},"    CompletableFuture\u003COrder> findById(String orderId);\n",[264,303,305],{"class":266,"line":304},7,[264,306,307],{},"}\n",[223,309,310,313],{},[235,311,312],{},"description"," is what the LLM reads to decide when to call the tool. When it is empty, the description resolves in order:",[315,316,317,337],"ol",{},[318,319,320,324,325,328,329,332,333,336],"li",{},[321,322,323],"strong",{},"The method's Javadoc."," An annotation processor shipped in ",[235,326,327],{},"kinotic-idl"," extracts each method's Javadoc main description at compile time (inline tags resolved, HTML stripped) into a ",[235,330,331],{},"META-INF\u002Fkinotic\u002Fdocs\u002F"," jar resource. Lookup follows the most specific declaration first — the implementation's override, the interface's declaration, then the interface's ancestors — so a function inherited from a generic CRUD base finds the doc written on the base, even across modules. Modules in this repo are wired automatically; an external project enables extraction with ",[235,334,335],{},"annotationProcessor 'org.kinotic:kinotic-idl'",".",[318,338,339,342,343,346,347,350],{},[321,340,341],{},"The function name",", split into a sentence — ",[235,344,345],{},"findByRepoFullName"," becomes ",[235,348,349],{},"Find by repo full name"," — so every tool stays individually recognizable even with no docs at all.",[223,352,353,356,357,360,361,363],{},[235,354,355],{},"title"," is a human-readable display name surfaced in tool listings. A tool's title is always ",[321,358,359],{},"two halves joined by a space"," — the service's, then the function's — and each half comes from the ",[235,362,355],{}," on the declaration that owns it, or is split out of the name it stands for:",[365,366,367,382],"table",{},[368,369,370],"thead",{},[371,372,373,376,379],"tr",{},[374,375],"th",{},[374,377,378],{},"Service half",[374,380,381],{},"Function half",[383,384,385,409],"tbody",{},[371,386,387,391,398],{},[388,389,390],"td",{},"Stated by",[388,392,393,395,396],{},[235,394,355],{}," on the type-level ",[235,397,241],{},[388,399,400,402,403,405,406],{},[235,401,355],{}," on the method's ",[235,404,241],{},"\u002F",[235,407,408],{},"@McpToolInfo",[371,410,411,414,417],{},[388,412,413],{},"Otherwise",[388,415,416],{},"the service interface's simple name, split",[388,418,419],{},"the function name, split",[255,421,426],{"className":422,"code":424,"language":425},[423],"language-text","ProjectService.findByRepoFullName                       → \"Project Service Find By Repo Full Name\"\n  + @McpTool(title = \"Find by GitHub Repo\") on the method → \"Project Service Find by GitHub Repo\"\n  + @McpTool(title = \"Projects\") on the interface         → \"Projects Find by GitHub Repo\"\n","text",[235,427,424],{"__ignoreMap":260},[223,429,430,431,434,435,438,439,441],{},"Carrying the service half on every tool is what keeps a title recognizable when many services expose a function of the same name (",[235,432,433],{},"save",", ",[235,436,437],{},"findById","), so a method's ",[235,440,355],{}," replaces only its own half. The title never affects the tool name.",[223,443,444,445,448,449,452],{},"The input schema's property names are the service interface's Java parameter names (",[235,446,447],{},"orderId"," above), retained in the class file at compile time — the same declaration the invocation binds against, so the published schema and the runtime binding cannot drift even when an implementation renames a parameter in its override. A single parameter can carry a different name in the schema with ",[235,450,451],{},"@Name(\"...\")",", declared on the interface.",[223,454,455,457,458,460],{},[235,456,241],{}," is honored in three places, mirroring how ",[235,459,245],{}," marks the interface while the implementation supplies the methods:",[462,463,464,481,487],"ul",{},[318,465,466,469,470,472,473,475,476,480],{},[321,467,468],{},"On the service interface itself"," — every function becomes a tool. A bare ",[235,471,241],{}," on the interface exposes a whole service, including functions inherited from a CRUD parent, with per-function descriptions from each function's Javadoc (or its name). Only ",[235,474,355],{}," is read here, as the service half of every tool's title; describing or hinting a service's functions is done per function (see ",[227,477,479],{"href":478},"#which-declaration-describes-a-function","Which declaration describes a function",").",[318,482,483,486],{},[321,484,485],{},"On an interface method"," — that method becomes a tool, and this is the declaration that describes it.",[318,488,489,492],{},[321,490,491],{},"On the implementation's override"," — same as an interface method, without touching the interface. This is how a single inherited function becomes a tool without redeclaring it.",[219,494,496],{"id":495},"describing-a-function-without-exposing-it","Describing a function without exposing it",[223,498,499,501,502,504,505,434,507,509,510,514],{},[235,500,408],{}," carries the same metadata as ",[235,503,241],{}," — ",[235,506,355],{},[235,508,312],{},", and the four hints — but never makes a tool. A base interface uses it to state what its functions ",[511,512,513],"em",{},"are",", leaving the decision to expose them to whichever service extends it:",[255,516,518],{"className":257,"code":517,"language":259,"meta":260,"style":260},"public interface CrudService\u003CT, ID> {\n\n    @McpToolInfo(readOnlyHint = true)\n    Future\u003CT> findById(ID id);\n\n    @McpToolInfo(destructiveHint = true, idempotentHint = true)\n    Future\u003CVoid> deleteById(ID id);\n}\n",[235,519,520,525,529,534,539,543,548,553],{"__ignoreMap":260},[264,521,522],{"class":266,"line":267},[264,523,524],{},"public interface CrudService\u003CT, ID> {\n",[264,526,527],{"class":266,"line":273},[264,528,289],{"emptyLinePlaceholder":288},[264,530,531],{"class":266,"line":279},[264,532,533],{},"    @McpToolInfo(readOnlyHint = true)\n",[264,535,536],{"class":266,"line":285},[264,537,538],{},"    Future\u003CT> findById(ID id);\n",[264,540,541],{"class":266,"line":292},[264,542,289],{"emptyLinePlaceholder":288},[264,544,545],{"class":266,"line":298},[264,546,547],{},"    @McpToolInfo(destructiveHint = true, idempotentHint = true)\n",[264,549,550],{"class":266,"line":304},[264,551,552],{},"    Future\u003CVoid> deleteById(ID id);\n",[264,554,556],{"class":266,"line":555},8,[264,557,307],{},[223,559,560,561,563,564,567,568,570,571,573],{},"A ",[235,562,245],{},"ed service that extends ",[235,565,566],{},"CrudService"," and carries a bare ",[235,569,241],{}," now serves correctly-hinted CRUD tools, while a service that carries no ",[235,572,241],{}," still exposes nothing.",[219,575,479],{"id":576},"which-declaration-describes-a-function",[223,578,579,580,582,583,586],{},"A function's title half, ",[235,581,312],{},", and hints all come from ",[321,584,585],{},"one"," declaration — the nearer of the two that can describe a function, chosen by whether it is there at all, never by what it says:",[315,588,589,595],{},[318,590,591,592,594],{},"A method-level ",[235,593,241],{}," (on the interface method or the implementation's override).",[318,596,597,598,600],{},"The function's ",[235,599,408],{},", wherever in its hierarchy it is declared.",[223,602,603,604,606],{},"The type-level ",[235,605,241],{}," is not on this list. It describes the service, not any one function, so it contributes only the service half of the title; a function it sweeps in with no declaration of its own falls back to what the function itself provides — its Javadoc for a description, its name for a title half and for hints.",[223,608,609,610,613,614,616,617,619,620,622,623,625],{},"Nothing is borrowed from the further declaration either: ",[235,611,612],{},"@McpTool(title = \"Store Entity\")"," on a method whose inherited ",[235,615,408],{}," sets a ",[235,618,312],{}," serves the Javadoc, not that ",[235,621,312],{},", because the method's own ",[235,624,241],{}," is what describes it.",[219,627,629],{"id":628},"hints","Hints",[223,631,632,633,434,636,434,639,434,642,645],{},"The four hints (",[235,634,635],{},"readOnlyHint",[235,637,638],{},"destructiveHint",[235,640,641],{},"idempotentHint",[235,643,644],{},"openWorldHint",") are served as MCP tool annotations. They follow the rule above with no string-shaped escape: a boolean has no blank value, so the winning declaration's hints are exactly what is served, including when it declares none.",[223,647,648,651,652,655,656,659,660,662,663,666],{},[235,649,650],{},"@McpTool(description = \"Appends an audit entry\")"," on a method named ",[235,653,654],{},"saveAuditEntry"," therefore serves ",[235,657,658],{},"destructiveHint: false"," — the annotation is present, so it decides, and it declares nothing destructive. And because a type-level ",[235,661,241],{}," supplies no hints, ",[235,664,665],{},"readOnlyHint = true"," there can no longer mislabel the mutating functions a sweep catches.",[223,668,669],{},"The name rules, reached only for a function with no declaration of its own. Every word counts, not just the leading verb, and the first matching rule wins:",[365,671,672,691],{},[368,673,674],{},[371,675,676,679,683,687],{},[374,677,678],{},"The name…",[374,680,681],{},[235,682,635],{},[374,684,685],{},[235,686,638],{},[374,688,689],{},[235,690,641],{},[383,692,693,747,770,815,868],{},[371,694,695,733,738,743],{},[388,696,697,698,434,700,434,703,434,706,434,709,434,712,434,715,434,718,434,721,434,724,434,727,434,730],{},"has a word that replaces or removes state — ",[235,699,433],{},[235,701,702],{},"update",[235,704,705],{},"put",[235,707,708],{},"set",[235,710,711],{},"upsert",[235,713,714],{},"delete",[235,716,717],{},"remove",[235,719,720],{},"clear",[235,722,723],{},"drop",[235,725,726],{},"purge",[235,728,729],{},"destroy",[235,731,732],{},"truncate",[388,734,735],{},[235,736,737],{},"false",[388,739,740],{},[235,741,742],{},"true",[388,744,745],{},[235,746,742],{},[371,748,749,758,762,766],{},[388,750,751,752,405,755],{},"ends in ",[235,753,754],{},"IfNotExist",[235,756,757],{},"IfNotExists",[388,759,760],{},[235,761,737],{},[388,763,764],{},[235,765,737],{},[388,767,768],{},[235,769,742],{},[371,771,772,803,807,811],{},[388,773,774,775,434,778,434,781,434,784,434,787,434,790,434,793,434,796,434,799,802],{},"has a word that adds or acts — ",[235,776,777],{},"create",[235,779,780],{},"add",[235,782,783],{},"insert",[235,785,786],{},"register",[235,788,789],{},"send",[235,791,792],{},"run",[235,794,795],{},"start",[235,797,798],{},"stop",[235,800,801],{},"retry",", …",[388,804,805],{},[235,806,737],{},[388,808,809],{},[235,810,737],{},[388,812,813],{},[235,814,737],{},[371,816,817,856,860,864],{},[388,818,819,820,434,823,434,826,434,829,434,832,434,835,434,838,434,841,434,844,434,847,434,850,434,853],{},"has a word that reads — ",[235,821,822],{},"find",[235,824,825],{},"get",[235,827,828],{},"list",[235,830,831],{},"search",[235,833,834],{},"count",[235,836,837],{},"query",[235,839,840],{},"read",[235,842,843],{},"fetch",[235,845,846],{},"is",[235,848,849],{},"has",[235,851,852],{},"exists",[235,854,855],{},"load",[388,857,858],{},[235,859,742],{},[388,861,862],{},[235,863,737],{},[388,865,866],{},[235,867,737],{},[371,869,870,873,877,881],{},[388,871,872],{},"matches nothing above",[388,874,875],{},[235,876,737],{},[388,878,879],{},[235,880,737],{},[388,882,883],{},[235,884,737],{},[255,886,889],{"className":887,"code":888,"language":425},[423],"deleteById               destructive, idempotent  — \"delete\"\ncreatePersonIfNotExist   idempotent               — the suffix rule is reached before \"create\"\npeopleCount              read-only                — \"count\", though it does not lead the name\ngetOrCreatePerson        all three false          — \"create\" is reached before \"get\"\nnotifyPeople             all three false          — no rule matches\n",[235,890,888],{"__ignoreMap":260},[223,892,893,894,897,898,900,901,903],{},"The adds-or-acts row exists only to be reached before the reads row. ",[235,895,896],{},"getOrCreatePerson"," both reads and creates, and matching it there is what stops ",[235,899,665],{}," from being served for a call that creates a person. All three ",[235,902,737],{}," is not \"unknown\" — it is the honest answer, and it tells a host the tool may modify something and is not safe to run unattended.",[223,905,906,907,909,910,917,918,920],{},"Every hint is always written to the wire, which matters most for ",[235,908,644],{},": ",[227,911,914,915],{"href":912,"rel":913},"https:\u002F\u002Fmodelcontextprotocol.io\u002Fspecification\u002F2025-11-25\u002Fserver\u002Ftools",[231],"MCP defaults it to ",[235,916,742],{},", so a tool that omits it is read as one that may call out to any external system, and hosts weigh that when deciding whether a call needs approval. A Kinotic function works against the platform's own data, so the served default is ",[235,919,737],{},"; a function that does reach a third-party system says so:",[255,922,924],{"className":257,"code":923,"language":259,"meta":260,"style":260},"@McpTool(openWorldHint = true)\nFuture\u003CProject> retryRepoInitialization(String projectId);\n",[235,925,926,931],{"__ignoreMap":260},[264,927,928],{"class":266,"line":267},[264,929,930],{},"@McpTool(openWorldHint = true)\n",[264,932,933],{"class":266,"line":273},[264,934,935],{},"Future\u003CProject> retryRepoInitialization(String projectId);\n",[223,937,938,939,941],{},"Nothing in a function's name says whether it calls out, so ",[235,940,644],{}," is never inferred — only a declaration sets it.",[223,943,944,945,434,948,951,952,434,955,434,958,961],{},"A function with a streaming return type (",[235,946,947],{},"Flux",[235,949,950],{},"Publisher",") cannot be a tool — registration fails with an error naming the function. Single-value async returns (",[235,953,954],{},"Future",[235,956,957],{},"CompletableFuture",[235,959,960],{},"Mono",") are fine.",[219,963,965],{"id":964},"tool-naming","Tool naming",[223,967,968],{},"The tool name is the XXHash128 of the service's qualified name plus the function, written in base 36:",[255,970,973],{"className":971,"code":972,"language":425},[423],"app.acme-org.orders-app~com.acme.OrderService\u002FfindById\n  → 5nwldv2gqljeygvmd1ywjl2z7\n",[235,974,972],{"__ignoreMap":260},[223,976,977],{},"Because a qualified name is unique system wide, so is its hash — every caller scope, including system participants who see every zone, gets an unambiguous listing. Names are never parsed back apart: resolution is a caller-scoped directory query, and authorization reads the zone from the tool's stored CRI.",[223,979,980,981,983,984,986],{},"A name is opaque, so ",[235,982,355],{}," and ",[235,985,312],{}," are what an LLM has to work with, and both are always served. Base 36 also bounds a name at 25 characters however long the service's package is, well inside the 128-character MCP limit.",[223,988,989,992],{},[235,990,991],{},"KinoticUtil.mcpToolName(qualifiedName, functionName)"," mints the name, so anything that needs to name a tool it did not read from a listing computes the same value the directory stored:",[255,994,996],{"className":257,"code":995,"language":259,"meta":260,"style":260},"KinoticUtil.mcpToolName(\"management-api~org.kinotic.management.api.services.ProjectService\", \"findByRepoFullName\")\n",[235,997,998],{"__ignoreMap":260},[264,999,1000],{"class":266,"line":267},[264,1001,995],{},[223,1003,1004,1005,1008],{},"Hashing is what keeps a name safe to pass through an LLM host. MCP permits characters in a tool name that hosts do not — a dot most commonly — and a host rewrites each one, then holds two names for the same tool: the one the server advertises and the one it calls. Any layer that compares the wrong pair breaks, and the failure is invisible from the server. A permission granted against the advertised name and checked against the callable one leaves a tool that prompts for approval, is approved, and reports that it still needs approval. Base 36 emits only ",[235,1006,1007],{},"[0-9a-z]",", so a host has nothing to rewrite.",[219,1010,1012],{"id":1011},"the-endpoint","The endpoint",[223,1014,1015,1017,1018,434,1021,434,1024,434,1027,1030,1031,1034,1035,1038],{},[235,1016,237],{}," speaks the stateless streamable-HTTP subset of MCP: JSON-RPC 2.0 with ",[235,1019,1020],{},"initialize",[235,1022,1023],{},"notifications\u002Finitialized",[235,1025,1026],{},"ping",[235,1028,1029],{},"tools\u002Flist",", and ",[235,1032,1033],{},"tools\u002Fcall",". There are no sessions — every request authenticates independently from its headers, exactly like a STOMP ",[235,1036,1037],{},"CONNECT",". Other verbs return 405, and JSON-RPC batches are rejected.",[219,1040,1042],{"id":1041},"authorization","Authorization",[223,1044,1045],{},"Two ways in, both landing on the same participant model:",[223,1047,1048,1051,1052,1055,1056,1059,1060,1063,1064,1067,1068,1072,1073,1076,1077,1080,1081,1084,1085,1088],{},[321,1049,1050],{},"OAuth 2.1 (MCP authorization spec)"," — what Claude connectors, Claude Code plugins, and other MCP hosts use. An unauthenticated request gets ",[235,1053,1054],{},"401"," with ",[235,1057,1058],{},"WWW-Authenticate: Bearer resource_metadata=\"…\u002F.well-known\u002Foauth-protected-resource\u002Fmcp\"",", from which a host discovers the authorization server (RFC 9728 + RFC 8414, both served from ",[235,1061,1062],{},"kinotic.domain.oauth.issuerBaseUrl",", which falls back to ",[235,1065,1066],{},"kinotic.domain.apiBaseUrl","), identifies itself with a ",[227,1069,1071],{"href":1070},"\u002Fplatform\u002Fsystem-security#client-identity-is-a-domain-not-a-string","Client ID Metadata Document"," URL, and runs the PKCE S256 authorization-code flow: ",[235,1074,1075],{},"GET \u002Fapi\u002Fauth\u002Foauth\u002Fauthorize"," sends the browser to the SPA's ",[235,1078,1079],{},"\u002Foauth\u002Fconsent"," page, where the signed-in user approves, and ",[235,1082,1083],{},"POST \u002Fapi\u002Fauth\u002Foauth\u002Ftoken"," (form-encoded) exchanges the single-use code for a one-hour access token plus a rotating 90-day refresh token (",[235,1086,1087],{},"offline_access"," is advertised, so hosts refresh without re-consent; refresh-token reuse revokes the whole token family).",[223,1090,1091,1092,1095,1096,434,1099,1102,1103,1106,1107,1110],{},"The issued token's ",[235,1093,1094],{},"sub"," is a delegate identity created when the user approves the consent — the host acts on that user's behalf, at the user's exact scope, as a ",[235,1097,1098],{},"SystemParticipant",[235,1100,1101],{},"OrganizationParticipant",", or ",[235,1104,1105],{},"ApplicationParticipant"," carrying ",[235,1108,1109],{},"onBehalfOf"," metadata, and sees the same visibility matrix as every other caller. Approving the same host again reuses the delegate, and disabling it revokes the host's access on its next request. A Claude Code plugin needs nothing beyond the server URL:",[255,1112,1116],{"className":1113,"code":1114,"language":1115,"meta":260,"style":260},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"mcpServers\": {\n    \"kinotic-os\": {\n      \"type\": \"http\",\n      \"url\": \"https:\u002F\u002Fapi.example.kinotic.ai\u002Fmcp\"\n    }\n  }\n}\n","json",[235,1117,1118,1124,1142,1157,1182,1201,1206,1211],{"__ignoreMap":260},[264,1119,1120],{"class":266,"line":267},[264,1121,1123],{"class":1122},"sMK4o","{\n",[264,1125,1126,1129,1133,1136,1139],{"class":266,"line":273},[264,1127,1128],{"class":1122},"  \"",[264,1130,1132],{"class":1131},"spNyl","mcpServers",[264,1134,1135],{"class":1122},"\"",[264,1137,1138],{"class":1122},":",[264,1140,1141],{"class":1122}," {\n",[264,1143,1144,1147,1151,1153,1155],{"class":266,"line":279},[264,1145,1146],{"class":1122},"    \"",[264,1148,1150],{"class":1149},"sBMFI","kinotic-os",[264,1152,1135],{"class":1122},[264,1154,1138],{"class":1122},[264,1156,1141],{"class":1122},[264,1158,1159,1162,1166,1168,1170,1173,1177,1179],{"class":266,"line":285},[264,1160,1161],{"class":1122},"      \"",[264,1163,1165],{"class":1164},"sbssI","type",[264,1167,1135],{"class":1122},[264,1169,1138],{"class":1122},[264,1171,1172],{"class":1122}," \"",[264,1174,1176],{"class":1175},"sfazB","http",[264,1178,1135],{"class":1122},[264,1180,1181],{"class":1122},",\n",[264,1183,1184,1186,1189,1191,1193,1195,1198],{"class":266,"line":292},[264,1185,1161],{"class":1122},[264,1187,1188],{"class":1164},"url",[264,1190,1135],{"class":1122},[264,1192,1138],{"class":1122},[264,1194,1172],{"class":1122},[264,1196,1197],{"class":1175},"https:\u002F\u002Fapi.example.kinotic.ai\u002Fmcp",[264,1199,1200],{"class":1122},"\"\n",[264,1202,1203],{"class":266,"line":298},[264,1204,1205],{"class":1122},"    }\n",[264,1207,1208],{"class":266,"line":304},[264,1209,1210],{"class":1122},"  }\n",[264,1212,1213],{"class":266,"line":555},[264,1214,307],{"class":1122},[223,1216,1217,1218,1221,1222,1225,1226,1229,1230,1233,1234,336],{},"There is no registration endpoint. Dynamic Client Registration (RFC 7591) is not supported: a ",[235,1219,1220],{},"client_id"," is the HTTPS URL of the client's own metadata document, which the gateway fetches and validates per authorization request. That URL must appear in ",[235,1223,1224],{},"kinotic.domain.oauth.allowedClientIds",", which ships with the document URLs of ",[227,1227,1228],{"href":1070},"Claude Code and the Claude connectors","; a host whose URL is absent gets ",[235,1231,1232],{},"400 {\"error\":\"invalid_request\"}"," from ",[235,1235,1236],{},"\u002Fapi\u002Fauth\u002Foauth\u002Fauthorize",[223,1238,1239,1242,1243,1246,1247,1249,1250,1252,1253,1256,1257,1259],{},[235,1240,1241],{},"issuerBaseUrl"," is separate from ",[235,1244,1245],{},"apiBaseUrl"," because the two are reached by different parties. A browser follows the OIDC callbacks built from ",[235,1248,1245],{},"; an MCP host's backend calls the token endpoint built from ",[235,1251,1241],{},", having never been near the browser. They are the same URL in any deployment the internet reaches directly, and differ where the gateway is public only through a tunnel or a separate ingress — a development gateway on ",[235,1254,1255],{},"localhost"," whose OAuth surface is tunnelled, for instance, keeps its OIDC callbacks (and its IdP app registrations) on ",[235,1258,1255],{}," while publishing a reachable token endpoint.",[223,1261,1262,1265,1266,405,1269,1272,1273,405,1276,1279,1280,1283],{},[321,1263,1264],{},"Static headers"," — for agent frameworks and scripts that can attach headers: ",[235,1267,1268],{},"clientId",[235,1270,1271],{},"clientSecret"," (plus ",[235,1274,1275],{},"organizationId",[235,1277,1278],{},"applicationId"," to select a scope), or ",[235,1281,1282],{},"Authorization: Bearer \u003Ckinotic-jwt>",". A bearer token takes its scope from the token's own claims, which are not cross-checked against any scope headers sent alongside it.",[223,1285,1286,1288,1289,1294,1295,1298,1299,1302,1303,1306,1307,1310,1311,1314,1315,1318,1319,1321],{},[235,1287,1029],{}," is paginated per the ",[227,1290,1293],{"href":1291,"rel":1292},"https:\u002F\u002Fmodelcontextprotocol.io\u002Fspecification\u002F2025-11-25\u002Fserver\u002Futilities\u002Fpagination",[231],"MCP pagination spec",": a response carrying ",[235,1296,1297],{},"nextCursor"," has more results, fetched by repeating the request with that opaque ",[235,1300,1301],{},"cursor","; an invalid cursor is rejected with ",[235,1304,1305],{},"-32602",". It returns the tools the authenticated participant may call, mirroring the zone send rules the gateway enforces at call time: a system participant sees every zone, an organization participant sees ",[235,1308,1309],{},"management-api","- and ",[235,1312,1313],{},"app-api","-zone tools, and an application participant sees its own ",[235,1316,1317],{},"app.\u003Corg>.\u003Capp>","-zone tools plus ",[235,1320,1313],{},"-zone tools. Offline services' tools are not listed.",[223,1323,1324,1326,1327,1329,1330,1333,1334,1337],{},[235,1325,1033],{}," dispatches through the same RPC path as every other service invocation, with the caller's participant as the sender. Resolution and dispatch are independently authorized — see ",[227,1328,172],{"href":173},". The ",[235,1331,1332],{},"arguments"," object is delivered to the service bound by parameter name, so the names in the tool's input schema are exactly the names the service binds. A call to an offline service or an invocation error returns an MCP tool error (",[235,1335,1336],{},"isError: true",") with the message as text content.",[1339,1340,1341],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}",{"title":260,"searchDepth":273,"depth":273,"links":1343},[1344,1345,1346,1347,1348,1349,1350,1351],{"id":221,"depth":273,"text":25},{"id":252,"depth":273,"text":253},{"id":495,"depth":273,"text":496},{"id":576,"depth":273,"text":479},{"id":628,"depth":273,"text":629},{"id":964,"depth":273,"text":965},{"id":1011,"depth":273,"text":1012},{"id":1041,"depth":273,"text":1042},"Expose published service functions as Model Context Protocol tools.","md",null,{},{"icon":179},{"title":176,"description":1352},"KaboxLO3a9OX10tR8R-CBYPdv-rG2tM7vywpSzoR2so",[1360,1362],{"title":172,"path":173,"stem":174,"description":1361,"icon":93,"children":-1},"The layered enforcement strategies applied to every request path in Kinotic OS.",{"title":181,"path":182,"stem":183,"description":1363,"icon":184,"children":-1},"Monitoring, tracing, and logging across your Kinotic applications.",1788549863676]