विषय पर बढ़ें

Completions

मशीनी अनुवाद

यह page अंग्रेज़ी documentation से अपने-आप अनुवादित किया गया है, और अंग्रेज़ी page ही प्रामाणिक version है। अगर कुछ गलत लगे, तो अनुवाद page बताता है कि इसकी सूचना कैसे दें।

आपके server के ऊपर UI बना रहा कोई client चाहता है कि user के टाइप करते ही argument values अपने आप पूरे हों: भाषाओं के नाम, repositories के नाम, file paths।

Completions वह तरीका है जिससे server ये सुझाव देता है।

कुछ ऐसा जो complete करने लायक हो

Completions ठीक दो चीज़ों पर लागू होते हैं: किसी prompt के arguments और किसी resource template के parameters। तो ऐसे server से शुरू करें जिसमें दोनों में से एक-एक हो:

server.py
from mcp.server import MCPServer

mcp = MCPServer("GitHub Explorer")


@mcp.resource("github://repos/{owner}/{repo}")
def github_repo(owner: str, repo: str) -> str:
    """A GitHub repository."""
    return f"Repository: {owner}/{repo}"


@mcp.prompt()
def review_code(language: str, code: str) -> str:
    """Review a snippet of code."""
    return f"Review this {language} code:\n{code}"

यहाँ अभी तक completions के बारे में कुछ नहीं है।

  • review_code एक language लेता है। user को यह अनुमान नहीं लगाना चाहिए कि आप कौन-सी वर्तनियाँ स्वीकार करते हैं।
  • github_repo एक owner और एक repo लेता है। दोनों के लिए free-text boxes रखना खराब form बनाता है।

Completion handler

@mcp.completion() से सजाया हुआ एक function जोड़ें:

server.py
from mcp.server import MCPServer
from mcp.types import Completion, CompletionArgument, CompletionContext, PromptReference, ResourceTemplateReference

mcp = MCPServer("GitHub Explorer")

LANGUAGES = ["go", "javascript", "python", "rust", "typescript"]


@mcp.resource("github://repos/{owner}/{repo}")
def github_repo(owner: str, repo: str) -> str:
    """A GitHub repository."""
    return f"Repository: {owner}/{repo}"


@mcp.prompt()
def review_code(language: str, code: str) -> str:
    """Review a snippet of code."""
    return f"Review this {language} code:\n{code}"


@mcp.completion()
async def handle_completion(
    ref: PromptReference | ResourceTemplateReference,
    argument: CompletionArgument,
    context: CompletionContext | None,
) -> Completion | None:
    if isinstance(ref, PromptReference) and argument.name == "language":
        return Completion(values=[lang for lang in LANGUAGES if lang.startswith(argument.value)])
    return None
  • हर server में एक ही handler होता है। हर completion request यहीं आती है, और जो complete हो रहा है उसके हिसाब से आप branch करते हैं।
  • इसे async def होना ज़रूरी है: SDK इसे await करता है।
  • इसे तीन arguments मिलते हैं:
  • ref: कौन-सा prompt या resource template, PromptReference या ResourceTemplateReference के रूप में। दोनों में फ़र्क isinstance से पता चलता है।
  • argument: argument.name वह argument है जो complete हो रहा है, argument.value वह है जो user ने अब तक टाइप किया है।
  • context: पहले से तय हो चुके arguments। अभी इसे नज़रअंदाज़ करें।
  • आप Completion(values=[...]) लौटाते हैं, या जब देने को कुछ न हो तो None

Tip

argument.value वह prefix है जो user ने टाइप किया है। SDK आपके लिए filter नहीं करता: जो कुछ आप values में रखते हैं, UI वही दिखाता है। startswith आपको खुद लिखना है।

इसे आज़माएँ

इसे Testing वाले in-memory Client से चलाएँ। client.complete() को ref=PromptReference(name="review_code") और argument={"name": "language", "value": "py"} के साथ call करें:

result.completion.values  # ['python']
  • ref वही reference type है जो आपके handler को मिलता है।
  • argument एक सादी dict है जिसमें ठीक दो keys हैं, name और value

खाली value भेजें और आपको पूरी सूची वापस मिलती है। lang.startswith("") हर भाषा के लिए true है:

result.completion.values  # ['go', 'javascript', 'python', 'rust', 'typescript']

code के बारे में पूछें (ऐसा argument जिसे handler नहीं पहचानता) और वह None लौटाता है, जिसे SDK खाली list में बदल देता है:

result.completion.values  # []

None का मतलब है "कोई सुझाव नहीं", error कभी नहीं। UI सादे text box पर लौट आता है।

एक capability जो आपने कभी declare नहीं की

handler register करना ही declaration है। कोई client जोड़ें और देखें:

client.server_capabilities.completions  # CompletionsCapability()

आपने completions कहीं नहीं लिखी। SDK ने handler देखा और आपके लिए capability declare कर दी। हर optional capability ऐसे ही काम करती है: handler ही declaration है। (तीनों primitives optional नहीं हैं: MCPServer उन्हें हमेशा declare करता है, handlers हों या न हों।)

Check

पहली server.py पर वापस जाएँ (जिसमें कोई handler नहीं है) और फिर भी उससे पूछें। call JSON-RPC error के साथ fail होती है:

Method not found

और client.server_capabilities.completions None है। capability का यही मतलब है: सही ढंग से बना client इसे जाँचता है और वह request कभी नहीं भेजता जिसका जवाब आप नहीं दे सकते।

एक-दूसरे पर निर्भर arguments

github://repos/{owner}/{repo} में दो parameters हैं, और repo के काम के values इस पर निर्भर करते हैं कि पहले कौन-सा owner चुना गया।

context इसी के लिए है। इसमें वे arguments होते हैं जो user पहले ही तय कर चुका है:

server.py
from mcp.server import MCPServer
from mcp.types import Completion, CompletionArgument, CompletionContext, PromptReference, ResourceTemplateReference

mcp = MCPServer("GitHub Explorer")

LANGUAGES = ["go", "javascript", "python", "rust", "typescript"]

REPOS_BY_OWNER = {
    "modelcontextprotocol": ["python-sdk", "typescript-sdk", "inspector"],
    "pydantic": ["pydantic", "pydantic-ai", "logfire"],
}


@mcp.resource("github://repos/{owner}/{repo}")
def github_repo(owner: str, repo: str) -> str:
    """A GitHub repository."""
    return f"Repository: {owner}/{repo}"


@mcp.prompt()
def review_code(language: str, code: str) -> str:
    """Review a snippet of code."""
    return f"Review this {language} code:\n{code}"


@mcp.completion()
async def handle_completion(
    ref: PromptReference | ResourceTemplateReference,
    argument: CompletionArgument,
    context: CompletionContext | None,
) -> Completion | None:
    if isinstance(ref, PromptReference) and argument.name == "language":
        return Completion(values=[lang for lang in LANGUAGES if lang.startswith(argument.value)])
    if isinstance(ref, ResourceTemplateReference) and argument.name == "repo":
        if context is None or context.arguments is None:
            return None
        repos = REPOS_BY_OWNER.get(context.arguments.get("owner", ""), [])
        return Completion(values=[repo for repo in repos if repo.startswith(argument.value)])
    return None
  • नई branch template के repo parameter के लिए चलती है।
  • context.arguments अब तक चुने गए values (यहाँ, owner) की dict[str, str] | None है।
  • अभी owner नहीं है तो कोई समझदार सुझाव भी नहीं, इसलिए handler None लौटाता है।

client ये तय हो चुके values context_arguments= से भेजता है। इस बार ref है ResourceTemplateReference(uri="github://repos/{owner}/{repo}")। खाली value के साथ repo माँगें और context_arguments={"owner": "modelcontextprotocol"} pass करें:

result.completion.values  # ['python-sdk', 'typescript-sdk', 'inspector']

context_arguments= हटा दें और वही call [] लौटाती है। जब तक handler को owner पता न हो, वह नहीं जान सकता कि कौन-से repos सुझाए।

Info

Completion total= और has_more= भी लेता है। इन्हें तब set करें जब values किसी लंबी सूची का एक हिस्सा हो, ताकि UI "और 200 बाकी" दिखा सके। ज़्यादातर handlers को इनकी कभी ज़रूरत नहीं पड़ती।

सारांश

  • Completions prompt arguments और resource template parameters के लिए सुझाव हैं। और कुछ नहीं।
  • @mcp.completion() वह एक handler register करता है। यह async def (ref, argument, context) -> Completion | None है।
  • isinstance(ref, ...) और argument.name पर branch करें। argument.value से filter खुद करें।
  • None खाली list बन जाता है। यह कभी error नहीं है।
  • context.arguments में पहले से तय values होती हैं; client उन्हें context_arguments= के रूप में देता है।
  • completions capability उसी पल आ जाती है जब आप handler register करते हैं। उसके बिना, request का जवाब Method not found है।

सुझाव तब काम आते हैं जब user अभी prompt या template भर ही रहा हो; किसी tool call के बीच में उससे सवाल पूछना हो तो आपको Elicitation चाहिए। text के अलावा tool जो कुछ लौटा सकता है वह सब Images, audio और icons में है।