Brunobkr/llama.cpp_AlgMor24_github
ΩFFFΣLLIa • llama.cpp • AlgMor24 ██████╗ ███████╗███████╗███████╗██╗ ██╗ ██╗ █████╗ ██╔═══██╗██╔════╝██╔════╝██╔════╝██║ ██║ ██║██╔══██╗ ██║ ██║█████╗ █████╗ █████╗ ██║ ██║ ██║███████║ ██║ ██║██╔══╝ ██╔══╝ ██╔══╝ ██║ ██║ ██║██╔══██║ ╚██████╔╝██║ ██║ ███████╗███████╗███████╗██║██║ ██║ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝╚══════╝╚═╝╚═╝ ╚═╝ High-Performance LLM / VLM Inference & Autonomous Agentic Ecosystem… See the full description on the dataset page: https://huggingface.co/datasets/Brunobkr/llama.cpp_AlgMor24_github.
03.1k
1> [!WARNING]2> Unfortunately i published the 1.0 by mistake...this package is currently under heavy development so there will be breaking changes in minors...threat this `1.x` as the `0.x` of any other package. Sorry for the disservice, every breaking will be properly labeled in the PR name.3 4# tmcp5 6A lightweight, schema-agnostic Model Context Protocol (MCP) server implementation with unified API design.7 8## Why tmcp?9 10tmcp offers significant advantages over the official MCP SDK:11 12- **🔄 Schema Agnostic**: Works with any validation library through adapters13- **📦 No Weird Dependencies**: Minimal footprint with only essential dependencies (looking at you `express`)14- **🎯 Unified API**: Consistent, intuitive interface across all MCP capabilities15- **🔌 Extensible**: Easy to add support for new schema libraries16- **⚡ Lightweight**: No bloat, just what you need17 18## Supported Schema Libraries19 20tmcp works with all major schema validation libraries through its adapter system:21 22- **Zod** - `@tmcp/adapter-zod`23- **Valibot** - `@tmcp/adapter-valibot`24- **ArkType** - `@tmcp/adapter-arktype`25- **Effect Schema** - `@tmcp/adapter-effect`26- **Zod v3** - `@tmcp/adapter-zod-v3`27 28## Installation29 30```bash31pnpm install tmcp32# Choose your preferred schema library adapter33pnpm install @tmcp/adapter-zod zod34```35 36## Quick Start37 38```javascript39import { McpServer } from 'tmcp';40import { ZodJsonSchemaAdapter } from '@tmcp/adapter-zod';41import { z } from 'zod';42 43const adapter = new ZodJsonSchemaAdapter();44const server = new McpServer(45 {46 name: 'my-server',47 version: '1.0.0',48 description: 'My awesome MCP server',49 },50 {51 adapter,52 capabilities: {53 tools: { listChanged: true },54 prompts: { listChanged: true },55 resources: { listChanged: true },56 },57 },58);59 60// While the adapter is optional (you can opt out by explicitly passing `adapter: undefined`)61// without an adapter the server cannot accept inputs, produce structured outputs, or request62// elicitations at all only do this for very simple servers.63 64// Define a tool with type-safe schema65server.tool(66 {67 name: 'calculate',68 description: 'Perform mathematical calculations',69 schema: z.object({70 operation: z.enum(['add', 'subtract', 'multiply', 'divide']),71 a: z.number(),72 b: z.number(),73 }),74 },75 async ({ operation, a, b }) => {76 switch (operation) {77 case 'add':78 return {79 content: [{ type: 'text', text: `${a} + ${b} = ${a + b}` }],80 };81 case 'subtract':82 return {83 content: [{ type: 'text', text: `${a} - ${b} = ${a - b}` }],84 };85 case 'multiply':86 return {87 content: [{ type: 'text', text: `${a} × ${b} = ${a * b}` }],88 };89 case 'divide':90 return {91 content: [{ type: 'text', text: `${a} ÷ ${b} = ${a / b}` }],92 };93 }94 },95);96 97// Process incoming requests98server.receive(request);99```100 101## Return helpers to skip boilerplate102 103Even the tiny calculator tool above ends with four near-identical `return { content: [{ type: 'text', text: ... }] }` blocks. Most MCP handlers have to build the same shapes: `CallToolResult`, `ReadResourceResult`, `GetPromptResult`, or `CompleteResult`. Writing them by hand is repetitive boilerplate. The `tmcp/utils` entry point ships a handful of factories that emit the correct shape for you.104 105```ts106import { tool, resource, prompt, complete } from 'tmcp/utils';107 108// Without helpers – lots of envelope noise109server.tool(110 { name: 'health-check', description: 'A health check tool' },111 async () => ({112 content: [{ type: 'text', text: 'ok' }],113 }),114);115 116// With helpers – just describe the payload once117server.tool(118 { name: 'health-check', description: 'A health check tool' },119 async () => tool.text('ok'),120);121 122server.tool(123 { name: 'profile-picture', description: 'Your profile picture' },124 async () => tool.media('image', await loadPng(), 'image/png'),125);126 127server.tool(128 {129 name: 'get-images',130 description:131 'Get the orders details and the images of all the products',132 schema: v.object({133 items: v.array(ItemsSchema),134 }),135 },136 async () => {137 const orders = await loadOrders();138 const images = await loadImages(orders);139 140 return tool.mix(141 [142 tool.text("Here's your images"),143 ...images.map((img) => tool.media('image', img)),144 ],145 orders,146 );147 },148);149 150server.resource(151 {152 name: 'readme',153 description: 'Top-level README',154 uri: 'file://README.md',155 },156 async (uri) =>157 resource.text(uri, await readFile(uri, 'utf8'), 'text/markdown'),158);159 160server.prompt(161 {162 name: 'explain',163 description: '',164 schema: v.object({ topic: v.string() }),165 },166 async ({ topic }) => prompt.message(`Explain ${topic} like I am five.`),167);168 169server.template(170 {171 name: 'users',172 description: 'Supports completion',173 uri: 'users/{id}',174 complete: {175 id: async (arg) =>176 complete.values(await findMatchingIds(arg), false),177 },178 },179 async (uri) => resource.blob(uri, await fetchUserBlob(uri)),180);181```182 183you can also compose different kind of tools with `tool.mix`184 185```ts186tool.mix([187 tool.text('Indexed workspace'),188 tool.media('image', png, 'image/png'),189]);190```191 192however be aware that193 1941. you can't pass `tool.structured` to `tool.mix` (but you can pass a second argument that will be the structured content)1952. if you pass even one `tool.error` to the `tool.mix` the whole return value will be an error196 197```ts198const structuredContent = {199 cool: true,200};201 202tool.mix(203 [204 tool.text(JSON.stringify(structuredContent)),205 tool.media('image', png, 'image/png'),206 ],207 structuredContent,208);209```210 211### Helper catalog212 213- `tool` – build `CallToolResult` objects via `text`, `error`, `media`, `resource`, `resourceLink`, `structured`, and `mix` (merge multiple kind of tool response).214- `resource` – return `ReadResourceResult` through `text`, `blob`, or `mix` (merge `resource.text` and `resource.blob`).215- `prompt` – generate `GetPromptResult` using `message` (single string), `messages` (array of strings), `text`, `media`, `resource`, `resourceLink` and `mix` (merge multiple kind of tool response).216- `complete` – create `CompleteResult` payloads with `values(list, hasMore?, total?)`.217 218All helpers are typed, so TypeScript will prevent you from returning malformed payloads while cutting down the repetitive envelopes that used to appear in almost every handler.219 220## Defining Tools, Prompts, Resources, and Templates in Separate Files221 222For better code organization and reusability, you can define your tools, prompts, resources, and templates in separate files using the `defineTool`, `definePrompt`, `defineResource`, and `defineTemplate` utilities. This approach allows you to:223 224- **Organize by feature**: Group related tools in feature-specific modules225- **Reuse across servers**: Share common tools between different MCP servers226 227### Example Structure228 229```javascript230// tools/calculator.js231import { defineTool } from 'tmcp/tool';232import { z } from 'zod';233 234export const addTool = defineTool(235 {236 name: 'add',237 description: 'Add two numbers',238 schema: z.object({239 a: z.number(),240 b: z.number(),241 }),242 },243 async ({ a, b }) => ({244 content: [{ type: 'text', text: `${a} + ${b} = ${a + b}` }],245 }),246);247 248export const multiplyTool = defineTool(249 {250 name: 'multiply',251 description: 'Multiply two numbers',252 schema: z.object({253 a: z.number(),254 b: z.number(),255 }),256 },257 async ({ a, b }) => ({258 content: [{ type: 'text', text: `${a} × ${b} = ${a * b}` }],259 }),260);261```262 263```javascript264// prompts/code-review.js265import { definePrompt } from 'tmcp/prompt';266import { z } from 'zod';267 268export const codeReviewPrompt = definePrompt(269 {270 name: 'code-review',271 description: 'Generate code review prompt',272 schema: z.object({273 code: z.string(),274 language: z.string(),275 }),276 },277 async ({ code, language }) => ({278 messages: [279 {280 role: 'user',281 content: {282 type: 'text',283 text: `Review this ${language} code:\n\n${code}`,284 },285 },286 ],287 }),288);289```290 291```javascript292// resources/files.js293import { defineResource } from 'tmcp/resource';294import { readFile } from 'node:fs/promises';295 296export const readmeResource = defineResource(297 {298 name: 'readme',299 description: 'Project README file',300 uri: 'file://README.md',301 },302 async (uri) => ({303 contents: [304 {305 uri,306 mimeType: 'text/markdown',307 text: await readFile(uri.replace('file://', ''), 'utf8'),308 },309 ],310 }),311);312```313 314```javascript315// templates/user-files.js316import { defineTemplate } from 'tmcp/template';317import { getUserFile } from '../api/users.js';318 319export const userFileTemplate = defineTemplate(320 {321 name: 'user-files',322 description: 'Access user files by ID',323 uri: 'users/{userId}/files/{filename}',324 complete: {325 userId: async (arg) => ({326 completion: {327 values: await searchUserIds(arg),328 hasMore: false,329 },330 }),331 },332 },333 async (uri, { userId, filename }) => ({334 contents: [335 {336 uri,337 mimeType: 'text/plain',338 text: await getUserFile(userId, filename),339 },340 ],341 }),342);343```344 345```javascript346// server.js347import { McpServer } from 'tmcp';348import { ZodJsonSchemaAdapter } from '@tmcp/adapter-zod';349import { addTool, multiplyTool } from './tools/calculator.js';350import { codeReviewPrompt } from './prompts/code-review.js';351import { readmeResource } from './resources/files.js';352import { userFileTemplate } from './templates/user-files.js';353 354const server = new McpServer(355 {356 name: 'my-server',357 version: '1.0.0',358 },359 {360 adapter: new ZodJsonSchemaAdapter(),361 },362);363 364// Register multiple tools at once365server.tools([addTool, multiplyTool]);366 367// Or register individually368server.tool(addTool);369 370// Same for prompts, resources, and templates371server.prompts([codeReviewPrompt]);372server.resources([readmeResource]);373server.templates([userFileTemplate]);374```375 376Adding the primitive to the server will error if the tool uses a different validation library than the one expressed in the adapter.377 378## API Reference379 380### McpServer381 382The main server class that handles MCP protocol communications.383 384#### Constructor385 386```javascript387new McpServer(serverInfo, options);388```389 390- `serverInfo`: Server metadata (name, version, description)391- `options`: Configuration object with optional adapter (for schema conversion) and capabilities392 393> [!IMPORTANT]394> While the adapter is optional (you can opt out by explicitly passing `adapter: undefined`) without an adapter the server cannot accept inputs, produce structured outputs, or request elicitations at all only do this for very simple servers.395 396#### Methods397 398##### `tool(definition, handler)` / `tools(definitions)`399 400Register one or more tools with optional schema validation.401 402```javascript403// Register a single tool inline404server.tool(405 {406 name: 'tool-name',407 description: 'Tool description',408 schema: yourSchema, // optional409 },410 async (input) => {411 // Tool implementation412 return result;413 },414);415 416// Register a tool created with defineTool417import { defineTool } from 'tmcp/tool';418 419const myTool = defineTool(420 {421 name: 'tool-name',422 description: 'Tool description',423 schema: yourSchema,424 },425 async (input) => {426 return result;427 },428);429 430server.tool(myTool);431 432// Register multiple tools at once433server.tools([tool1, tool2, tool3]);434```435 436`sessionInfo` contains the latest `clientCapabilities`, `clientInfo`, and `logLevel` reported by the connected client. Built-in transports populate it automatically, so every handler can branch on client features without additional bookkeeping.437 438##### `prompt(definition, handler)` / `prompts(definitions)`439 440Register one or more prompt templates with optional schema validation.441 442```javascript443// Register a single prompt inline444server.prompt(445 {446 name: 'prompt-name',447 description: 'Prompt description',448 schema: yourSchema, // optional449 complete: (arg, context) => ['completion1', 'completion2'] // optional450 },451 async (input) => {452 // Prompt implementation453 return { messages: [...] };454 }455);456 457// Register a prompt created with definePrompt458import { definePrompt } from 'tmcp/prompt';459 460const myPrompt = definePrompt(461 {462 name: 'prompt-name',463 description: 'Prompt description',464 schema: yourSchema,465 },466 async (input) => {467 return { messages: [...] };468 }469);470 471server.prompt(myPrompt);472 473// Register multiple prompts at once474server.prompts([prompt1, prompt2, prompt3]);475```476 477##### `resource(definition, handler)` / `resources(definitions)`478 479Register one or more static resources.480 481```javascript482// Register a single resource inline483server.resource(484 {485 name: 'resource-name',486 description: 'Resource description',487 uri: 'file://path/to/resource'488 },489 async (uri, params) => {490 // Resource implementation491 return { contents: [...] };492 }493);494 495// Register a resource created with defineResource496import { defineResource } from 'tmcp/resource';497 498const myResource = defineResource(499 {500 name: 'resource-name',501 description: 'Resource description',502 uri: 'file://path/to/resource'503 },504 async (uri) => {505 return { contents: [...] };506 }507);508 509server.resource(myResource);510 511// Register multiple resources at once512server.resources([resource1, resource2, resource3]);513```514 515##### `template(definition, handler)` / `templates(definitions)`516 517Register one or more URI templates for dynamic resources.518 519```javascript520// Register a single template inline521server.template(522 {523 name: 'template-name',524 description: 'Template description',525 uri: 'file://path/{id}/resource',526 complete: (arg, context) => ['id1', 'id2'] // optional527 },528 async (uri, params) => {529 // Template implementation using params.id530 return { contents: [...] };531 }532);533 534// Register a template created with defineTemplate535import { defineTemplate } from 'tmcp/template';536 537const myTemplate = defineTemplate(538 {539 name: 'template-name',540 description: 'Template description',541 uri: 'file://path/{id}/resource',542 },543 async (uri, params) => {544 return { contents: [...] };545 }546);547 548server.template(myTemplate);549 550// Register multiple templates at once551server.templates([template1, template2, template3]);552```553 554##### `withContext<T>()`555 556Specify the type of custom context for type-safe access to application-specific data.557 558```javascript559interface MyCustomContext {560 userId: string;561 permissions: string[];562 database: DatabaseConnection;563}564 565const server = new McpServer(serverInfo, options).withContext<MyCustomContext>();566 567// Now you can access typed custom context in handlers568server.tool(569 {570 name: 'get-user-data',571 description: 'Get current user data',572 },573 async () => {574 const { userId, database } = server.ctx.custom!;575 const userData = await database.users.findById(userId);576 return {577 content: [{ type: 'text', text: JSON.stringify(userData) }],578 };579 },580);581```582 583##### `ctx`584 585Access the current request context, including session ID, auth info, client metadata, and custom context.586 587```javascript588server.tool(589 {590 name: 'context-aware-tool',591 description: 'Tool that uses request context',592 },593 async () => {594 const { sessionId, auth, sessionInfo, custom } = server.ctx;595 596 if (!custom?.userId) {597 throw new Error('User authentication required');598 }599 600 if (sessionInfo?.clientInfo) {601 console.log(`Serving ${sessionInfo.clientInfo.name}`);602 }603 604 return {605 content: [606 {607 type: 'text',608 text: `Hello user ${custom.userId} in session ${sessionId}`,609 },610 ],611 };612 },613);614```615 616##### `receive(request, context?)`617 618Process an incoming MCP request with optional context.619 620```javascript621// Basic usage622const response = server.receive(jsonRpcRequest);623 624// With custom context (typically used by transports)625const response = server.receive(jsonRpcRequest, {626 sessionId: 'session-123',627 auth: authInfo,628 sessionInfo: {629 clientCapabilities,630 clientInfo,631 logLevel,632 },633 custom: customContextData,634});635```636 637##### `request({ method, params })`638 639Send a raw JSON-RPC request to the connected client. This lets you call640experimental MCP APIs or proprietary extensions before they gain a dedicated641method on `McpServer` or to send a request with a custom JSON-schema that is not expressible with your validation library.642 643```javascript644const response = await server.request({645 method: 'elicitation/create',646 params: {647 message: 'Describe the deployment plan',648 requestedSchema: {649 type: 'object',650 required: ['region', 'replicas', 'features'],651 properties: {652 region: {653 type: 'string',654 enum: ['us-east-1', 'us-west-2', 'eu-central-1'],655 },656 replicas: { type: 'integer', minimum: 1, maximum: 20 },657 features: {658 type: 'array',659 items: {660 type: 'string',661 enum: ['canary', 'observability', 'autoscaling'],662 },663 minItems: 1,664 },665 },666 },667 },668});669```670 671- `method`: Fully qualified MCP client method name672- `params` (optional): JSON-RPC params object/array accepted by that method673 674Handle the resolved payload like any other JSON-RPC response—cast or (better) validate675as needed when using this escape hatch.676 677##### `elicitation(schema)`678 679Request client elicitation with schema validation.680 681```javascript682const result = await server.elicitation(schema);683```684 685##### `message(request)`686 687Request language model sampling from the client.688 689```javascript690const result = await server.message({691 messages: [692 {693 role: 'user',694 content: { type: 'text', text: 'Hello!' },695 },696 ],697});698```699 700##### `refreshRoots()`701 702Refresh the roots list from the client.703 704```javascript705await server.refreshRoots();706console.log(server.roots); // Access current roots707```708 709##### `changed(type, id)`710 711Send notifications for subscriptions. When you call `changed('resource', uri)` the server now emits a broadcast notification, and the built-in transports look up which sessions subscribed to that URI before delivering it.712 713```javascript714server.changed('resource', 'file://path/to/resource');715```716 717##### `progress(progress, total?, message?)`718 719Report progress during long-running operations. Progress notifications are only sent when a progress token is provided by the client in the request's `_meta.progressToken` field.720 721```javascript722server.tool(723 {724 name: 'process-large-file',725 description: 'Process a large file with progress updates',726 },727 async (input) => {728 const totalSteps = 100;729 730 for (let i = 0; i <= totalSteps; i++) {731 // Report progress with current step, total steps, and optional message732 server.progress(733 i,734 totalSteps,735 `Processing step ${i}/${totalSteps}`,736 );737 738 // Simulate work739 await processStep(i);740 }741 742 return {743 content: [{ type: 'text', text: 'Processing complete!' }],744 };745 },746);747```748 749**Parameters:**750 751- `progress` (number): Current progress value (should be ≤ total and always increase)752- `total` (number, optional): Maximum progress value (defaults to 1)753- `message` (string, optional): Descriptive message about current progress754 755**Notes:**756 757- Progress notifications are only sent when the client provides a `progressToken` in the request758- Progress values should be monotonically increasing within a single operation759- Each session maintains its own progress context760 761##### `log(level, data, logger?)`762 763Send log messages to connected clients when logging is enabled.764 765```javascript766// Log at different severity levels767server.log('info', 'Server started successfully');768server.log('warning', 'Configuration missing, using defaults');769server.log('error', 'Failed to connect to database', 'database-logger');770```771 772**Parameters:**773 774- `level` (string): Log level ('debug', 'info', 'notice', 'warning', 'error', 'critical', 'alert', 'emergency')775- `data` (any): Data to log776- `logger` (string, optional): Logger name/category777 778##### `on(event, callback)`779 780Listen to server events.781 782Available events:783 784- `initialize` – fired after the client handshake with the parsed initialize payload785- `send` – emitted for point-to-point responses/notifications destined for the active session786- `broadcast` – emitted for fan-out notifications (for example `notifications/resources/updated`)787- `subscription` – raised when the client subscribes to a resource URI788- `loglevelchange` – fired when the client requests a different log level789 790```javascript791server.on('initialize', ({ clientInfo }) => {792 console.log('Client initialized:', clientInfo?.name);793});794 795server.on('send', ({ request }) => {796 console.log('Sending message:', request);797});798 799server.on('broadcast', ({ request }) => {800 console.log('Broadcasting:', request.method);801});802```803 804## Advanced Examples805 806### Progress Reporting807 808Provide real-time progress updates for long-running operations:809 810```javascript811server.tool(812 {813 name: 'analyze-codebase',814 description: 'Analyze a large codebase with progress tracking',815 schema: z.object({816 path: z.string(),817 includeTests: z.boolean().default(false),818 }),819 },820 async ({ path, includeTests }) => {821 // Discover files to analyze822 const files = await discoverFiles(path, includeTests);823 const totalFiles = files.length;824 825 server.progress(0, totalFiles, 'Starting analysis...');826 827 const results = [];828 for (let i = 0; i < files.length; i++) {829 const file = files[i];830 831 // Update progress with current file being processed832 server.progress(833 i + 1,834 totalFiles,835 `Analyzing ${file} (${i + 1}/${totalFiles})`,836 );837 838 // Analyze the file839 const analysis = await analyzeFile(file);840 results.push(analysis);841 }842 843 // Final progress update844 server.progress(totalFiles, totalFiles, 'Analysis complete!');845 846 return {847 content: [848 {849 type: 'text',850 text: `Analyzed ${totalFiles} files. Found ${results.length} issues.`,851 },852 ],853 structuredContent: {854 totalFiles,855 results,856 issues: results.length,857 },858 };859 },860);861 862// Progress also works in prompts and resources863server.prompt(864 {865 name: 'generate-report',866 description: 'Generate a comprehensive report',867 schema: z.object({868 sections: z.array(z.string()),869 }),870 },871 async ({ sections }) => {872 const messages = [];873 874 for (let i = 0; i < sections.length; i++) {875 server.progress(876 i,877 sections.length,878 `Generating section: ${sections[i]}`,879 );880 881 const content = await generateSection(sections[i]);882 messages.push({883 role: 'user',884 content: { type: 'text', text: content },885 });886 }887 888 server.progress(sections.length, sections.length, 'Report complete');889 return { messages };890 },891);892```893 894### Client Interaction Features895 896```javascript897// Elicitation - Request structured data from client898const userData = await server.elicitation(899 z.object({900 name: z.string(),901 age: z.number(),902 preferences: z.array(z.string()),903 }),904);905 906// Message sampling - Request AI responses907const aiResponse = await server.message({908 messages: [909 {910 role: 'user',911 content: { type: 'text', text: 'Explain quantum computing' },912 },913 ],914 maxTokens: 100,915});916 917// Roots management - Access client's filesystem roots918await server.refreshRoots();919console.log('Available roots:', server.roots);920```921 922### Event Handling923 924```javascript925// Listen to server events926server.on('initialize', (data) => {927 console.log('Client capabilities:', data.capabilities);928});929 930server.on('send', ({ request }) => {931 console.log('Outgoing request:', request.method);932});933 934server.on('broadcast', ({ request }) => {935 if (request.method === 'notifications/resources/updated') {936 console.log('Resource updated:', request.params.uri);937 }938});939```940 941### Resource Subscriptions942 943```javascript944// Subscribe to resource changes945server.resource(946 {947 name: 'file-watcher',948 description: 'Watch file changes',949 uri: 'file://watched/file.txt',950 },951 async (uri) => {952 return {953 contents: [954 {955 uri,956 mimeType: 'text/plain',957 text: await readFile(uri),958 },959 ],960 };961 },962);963 964// Notify subscribers when resource changes965server.changed('resource', 'file://watched/file.txt');966```967 968### Completion API969 970The completion API allows you to provide auto-completion suggestions for prompt and template parameters. Completion functions return an object with `completion` containing `values`, `total`, and `hasMore` properties.971 972#### Completion Response Format973 974```javascript975{976 completion: {977 values: string[], // Array of completion values (max 100 items)978 total?: number, // Total number of available completions979 hasMore?: boolean // Whether there are more completions available980 }981}982```983 984#### Prompt Parameter Completion985 986```javascript987server.prompt(988 {989 name: 'story-generator',990 description: 'Generate a story with specific parameters',991 schema: z.object({992 genre: z.string(),993 length: z.enum(['short', 'medium', 'long']),994 character: z.string(),995 }),996 complete: {997 genre: (arg, context) => {998 const genres = [999 'fantasy',1000 'sci-fi',1001 'mystery',1002 'romance',1003 'thriller',1004 ];1005 const filtered = genres.filter((g) =>1006 g.startsWith(arg.toLowerCase()),1007 );1008 1009 return {1010 completion: {1011 values: filtered,1012 total: filtered.length,1013 hasMore: false,1014 },1015 };1016 },1017 length: (arg, context) => {1018 const lengths = ['short', 'medium', 'long'];1019 const filtered = lengths.filter((l) => l.includes(arg));1020 1021 return {1022 completion: {1023 values: filtered,1024 total: filtered.length,1025 hasMore: false,1026 },1027 };1028 },1029 character: (arg, context) => {1030 // Dynamic completion based on genre1031 const characters = {1032 fantasy: ['wizard', 'dragon', 'knight', 'elf'],1033 'sci-fi': ['robot', 'alien', 'cyborg', 'space-explorer'],1034 mystery: ['detective', 'suspect', 'witness', 'victim'],1035 default: ['hero', 'villain', 'sidekick', 'mentor'],1036 };1037 1038 const genre = context.params?.genre || 'default';1039 const charList = characters[genre] || characters.default;1040 const filtered = charList.filter((c) => c.includes(arg));1041 1042 return {1043 completion: {1044 values: filtered,1045 total: filtered.length,1046 hasMore: false,1047 },1048 };1049 },1050 },1051 },1052 async (input) => {1053 return {1054 description: `A ${input.length} ${input.genre} story featuring a ${input.character}`,1055 messages: [1056 {1057 role: 'user',1058 content: {1059 type: 'text',1060 text: `Write a ${input.length} ${input.genre} story featuring a ${input.character}.`,1061 },1062 },1063 ],1064 };1065 },1066);1067```1068 1069#### Resource Template Completion1070 1071```javascript1072server.template(1073 {1074 name: 'user-profile',1075 description: 'Get user profile by ID',1076 uri: 'users/{userId}/profile',1077 complete: {1078 userId: (arg, context) => {1079 // Filter users based on the current input1080 const allUsers = ['user1', 'user2', 'user3', 'admin-user'];1081 const filtered = allUsers.filter((id) => id.includes(arg));1082 1083 return {1084 completion: {1085 values: filtered.slice(0, 10), // Limit to 10 results1086 total: filtered.length,1087 hasMore: filtered.length > 10,1088 },1089 };1090 },1091 },1092 },1093 async (uri, params) => {1094 const user = await getUserById(params.userId);1095 return {1096 contents: [1097 {1098 uri,1099 mimeType: 'application/json',1100 text: JSON.stringify(user),1101 },1102 ],1103 };1104 },1105);1106```1107 1108#### Advanced Completion Examples1109 1110```javascript1111// Completion with async data fetching1112server.template(1113 {1114 name: 'project-files',1115 description: 'Access project files by path',1116 uri: 'projects/{projectId}/files/{filePath}',1117 complete: {1118 projectId: (arg, context) => {1119 // Static list of project IDs1120 const projects = ['web-app', 'mobile-app', 'api-server'];1121 const filtered = projects.filter((p) => p.includes(arg));1122 1123 return {1124 completion: {1125 values: filtered,1126 total: filtered.length,1127 hasMore: false,1128 },1129 };1130 },1131 filePath: async (arg, context) => {1132 // Dynamic file path completion based on project1133 const projectId = context.params?.projectId;1134 if (!projectId) {1135 return {1136 completion: {1137 values: [],1138 total: 0,1139 hasMore: false,1140 },1141 };1142 }1143 1144 // Simulate fetching files from filesystem1145 const files = await getProjectFiles(projectId);1146 const filtered = files.filter((f) => f.includes(arg));1147 1148 return {1149 completion: {1150 values: filtered.slice(0, 20),1151 total: filtered.length,1152 hasMore: filtered.length > 20,1153 },1154 };1155 },1156 },1157 },1158 async (uri, params) => {1159 const content = await readProjectFile(1160 params.projectId,1161 params.filePath,1162 );1163 return {1164 contents: [1165 {1166 uri,1167 mimeType: 'text/plain',1168 text: content,1169 },1170 ],1171 };1172 },1173);1174 1175// Completion with pagination support1176server.prompt(1177 {1178 name: 'search-documents',1179 description: 'Search through large document collection',1180 schema: z.object({1181 query: z.string(),1182 category: z.string(),1183 }),1184 complete: {1185 category: (arg, context) => {1186 // Large category list with pagination1187 const allCategories = generateCategoryList(); // Assume this returns 500+ items1188 const filtered = allCategories.filter((c) =>1189 c.toLowerCase().includes(arg.toLowerCase()),1190 );1191 1192 return {1193 completion: {1194 values: filtered.slice(0, 50), // Show first 50 matches1195 total: filtered.length,1196 hasMore: filtered.length > 50,1197 },1198 };1199 },1200 },