|
| 1 | +--- |
| 2 | +title: Argument Completion |
| 3 | +sidebarTitle: Completions |
| 4 | +description: Suggest values for prompt arguments and resource template parameters as the user types. |
| 5 | +icon: list-check |
| 6 | +--- |
| 7 | + |
| 8 | +import { VersionBadge } from "/snippets/version-badge.mdx" |
| 9 | + |
| 10 | +<VersionBadge version="4.0.0" /> |
| 11 | + |
| 12 | +Argument completion lets a server suggest values while a user fills in a prompt argument or a resource template parameter. As the user types, the client sends a `completion/complete` request naming the prompt or template, the argument being completed, and the partial value so far. The server answers with candidate strings, which the client offers as autocomplete suggestions. |
| 13 | + |
| 14 | +This is the server side of the feature. A client requests completions with [`Client.complete()`](/clients/client); this page covers how a server answers. |
| 15 | + |
| 16 | +## Register a completion handler |
| 17 | + |
| 18 | +A server has a single completion handler, registered with the `@mcp.completion` decorator. The handler receives every completion request and switches on which reference and argument is being completed. |
| 19 | + |
| 20 | +```python |
| 21 | +from fastmcp import FastMCP |
| 22 | +from mcp_types import PromptReference |
| 23 | + |
| 24 | +mcp = FastMCP("Completion Server") |
| 25 | + |
| 26 | + |
| 27 | +@mcp.prompt |
| 28 | +def write_poem(theme: str) -> str: |
| 29 | + return f"Write a poem about {theme}" |
| 30 | + |
| 31 | + |
| 32 | +@mcp.completion |
| 33 | +def complete(ref, argument, context): |
| 34 | + if isinstance(ref, PromptReference) and ref.name == "write_poem": |
| 35 | + if argument.name == "theme": |
| 36 | + options = ["nature", "love", "adventure"] |
| 37 | + return [o for o in options if o.startswith(argument.value)] |
| 38 | + return None |
| 39 | +``` |
| 40 | + |
| 41 | +The handler is called with three values: |
| 42 | + |
| 43 | +- `ref`: which component is being completed — a `PromptReference` (carrying the prompt `name`) or a `ResourceTemplateReference` (carrying the template `uri`). |
| 44 | +- `argument`: a `CompletionArgument` with the argument `name` and the partial `value` typed so far. |
| 45 | +- `context`: an optional `CompletionContext` carrying the values of arguments the user has already supplied (see [Using already-supplied arguments](#using-already-supplied-arguments)). |
| 46 | + |
| 47 | +Filter your candidates against `argument.value` so the suggestions narrow as the user types. Returning `None` means "I have no suggestions for this reference and argument" — the client receives an empty list, which is the correct answer for a reference the server does not recognize. |
| 48 | + |
| 49 | +<Tip> |
| 50 | +Registering a completion handler declares the server's completions capability during the handshake. A server with no handler does not advertise the capability, and a client that checks capabilities before calling will skip completion requests entirely. This works the same way on both the handshake and modern protocol eras. |
| 51 | +</Tip> |
| 52 | + |
| 53 | +## Completing resource template parameters |
| 54 | + |
| 55 | +The same handler answers completion for resource template parameters. A `ResourceTemplateReference` identifies the template by its URI template, and `argument.name` is the parameter being completed. |
| 56 | + |
| 57 | +```python |
| 58 | +from fastmcp import FastMCP |
| 59 | +from mcp_types import ResourceTemplateReference |
| 60 | + |
| 61 | +mcp = FastMCP("Completion Server") |
| 62 | + |
| 63 | +REPOS = ["fastmcp", "prefect", "marvin"] |
| 64 | + |
| 65 | + |
| 66 | +@mcp.resource("github://{owner}/{repo}") |
| 67 | +def repo_readme(owner: str, repo: str) -> str: |
| 68 | + return f"README for {owner}/{repo}" |
| 69 | + |
| 70 | + |
| 71 | +@mcp.completion |
| 72 | +def complete(ref, argument, context): |
| 73 | + if isinstance(ref, ResourceTemplateReference): |
| 74 | + if ref.uri == "github://{owner}/{repo}" and argument.name == "repo": |
| 75 | + return [r for r in REPOS if r.startswith(argument.value)] |
| 76 | + return None |
| 77 | +``` |
| 78 | + |
| 79 | +Because a single handler answers for every prompt and template, a server that completes several components branches on `ref` first, then on `argument.name`. Grouping the branches by reference keeps the handler readable as it grows. |
| 80 | + |
| 81 | +## Using already-supplied arguments |
| 82 | + |
| 83 | +Completions often depend on values the user has already entered. A repository suggestion, for example, depends on which owner was chosen. The client sends those resolved values in the completion context, and the handler reads them from `context.arguments`. |
| 84 | + |
| 85 | +```python |
| 86 | +from fastmcp import FastMCP |
| 87 | +from mcp_types import ResourceTemplateReference |
| 88 | + |
| 89 | +mcp = FastMCP("Completion Server") |
| 90 | + |
| 91 | +REPOS_BY_OWNER = { |
| 92 | + "prefecthq": ["fastmcp", "prefect", "marvin"], |
| 93 | + "python": ["cpython", "mypy"], |
| 94 | +} |
| 95 | + |
| 96 | + |
| 97 | +@mcp.resource("github://{owner}/{repo}") |
| 98 | +def repo_readme(owner: str, repo: str) -> str: |
| 99 | + return f"README for {owner}/{repo}" |
| 100 | + |
| 101 | + |
| 102 | +@mcp.completion |
| 103 | +def complete(ref, argument, context): |
| 104 | + if isinstance(ref, ResourceTemplateReference) and argument.name == "repo": |
| 105 | + owner = context.arguments.get("owner") if context and context.arguments else None |
| 106 | + repos = REPOS_BY_OWNER.get(owner or "", []) |
| 107 | + return [r for r in repos if r.startswith(argument.value)] |
| 108 | + return None |
| 109 | +``` |
| 110 | + |
| 111 | +Here the suggestions for `repo` are scoped to the `owner` the user already selected. The context is only present once at least one argument has been resolved, so guard against `context` being `None`. |
| 112 | + |
| 113 | +## Returning results |
| 114 | + |
| 115 | +A handler may return any of three things: |
| 116 | + |
| 117 | +- A list of strings — the simplest form, wrapped into a completion response automatically. |
| 118 | +- `None` — treated as an empty completion, for references and arguments the handler does not recognize. |
| 119 | +- A `Completion` object — when you want to include pagination hints alongside the values. |
| 120 | + |
| 121 | +The MCP protocol caps a single response at 100 values. When more candidates exist, return a `Completion` and set `total` (how many candidates match in all) and `has_more` (whether values were truncated) so the client can indicate that the list is partial. |
| 122 | + |
| 123 | +```python |
| 124 | +from fastmcp import FastMCP |
| 125 | +from mcp_types import Completion, PromptReference |
| 126 | + |
| 127 | +mcp = FastMCP("Completion Server") |
| 128 | + |
| 129 | +ALL_CITIES = ["Paris", "Prague", "Portland", "Phoenix", "Perth"] |
| 130 | + |
| 131 | + |
| 132 | +@mcp.prompt |
| 133 | +def pick_city(city: str) -> str: |
| 134 | + return f"Tell me about {city}" |
| 135 | + |
| 136 | + |
| 137 | +def search_cities(prefix: str) -> list[str]: |
| 138 | + # A real lookup might return thousands of matches; ALL_CITIES stands in. |
| 139 | + return [c for c in ALL_CITIES if c.startswith(prefix)] |
| 140 | + |
| 141 | + |
| 142 | +@mcp.completion |
| 143 | +def complete(ref, argument, context): |
| 144 | + if isinstance(ref, PromptReference) and argument.name == "city": |
| 145 | + matches = search_cities(argument.value) |
| 146 | + return Completion( |
| 147 | + values=matches[:100], |
| 148 | + total=len(matches), |
| 149 | + has_more=len(matches) > 100, |
| 150 | + ) |
| 151 | + return None |
| 152 | +``` |
| 153 | + |
| 154 | +## Accessing the request context |
| 155 | + |
| 156 | +A completion handler may be sync or async, and it can reach the active request through FastMCP's dependency functions the same way any handler does. Use [`get_context()`](/servers/context) to access session information, authentication, or server state while computing suggestions. |
| 157 | + |
| 158 | +```python |
| 159 | +from fastmcp import FastMCP |
| 160 | +from fastmcp.server.dependencies import get_context |
| 161 | +from mcp_types import PromptReference |
| 162 | + |
| 163 | +mcp = FastMCP("Completion Server") |
| 164 | + |
| 165 | + |
| 166 | +@mcp.completion |
| 167 | +async def complete(ref, argument, context): |
| 168 | + ctx = get_context() |
| 169 | + await ctx.debug(f"Completing {argument.name!r} for {ref}") |
| 170 | + ... |
| 171 | +``` |
| 172 | + |
| 173 | +## Authorization |
| 174 | + |
| 175 | +Completion runs behind the server's connection-level authentication: an unauthenticated client never reaches the handler. It is independent of per-component `auth=`, though. FastMCP does not resolve the referenced prompt or resource template, so a completion request is not filtered by that component's visibility the way `prompts/get` or a resource read is — the single handler answers for whatever reference the client names. |
| 176 | + |
| 177 | +A completion response carries only candidate strings for one argument, never component content or schema, so this exposes nothing about a hidden component on its own. If a handler computes candidates that should themselves be restricted — matching a prompt hidden from unauthorized callers, say — check the auth context inside the handler (via [`get_context()`](/servers/context)) and return `None` when the caller is not permitted. |
0 commit comments