- Notifications
You must be signed in to change notification settings - Fork 3.8k
/
Copy pathmcp_server_with_sampling.py
116 lines (98 loc) · 3.85 KB
/
mcp_server_with_sampling.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# /// script # noqa: CPY001
# dependencies = [
# "semantic-kernel[mcp]",
# ]
# ///
# Copyright (c) Microsoft. All rights reserved.
importlogging
fromtypingimportAnnotated, Any
importanyio
frommcpimporttypes
frommcp.server.lowlevelimportServer
frommcp.server.stdioimportstdio_server
fromsemantic_kernelimportKernel
fromsemantic_kernel.functionsimportkernel_function
fromsemantic_kernel.prompt_templateimportInputVariable, KernelPromptTemplate, PromptTemplateConfig
logger=logging.getLogger(__name__)
"""
This sample demonstrates how to expose your Semantic Kernel `kernel` instance as a MCP server, with the a function
that uses sampling (see the docs: https://modelcontextprotocol.io/docs/concepts/sampling) to generate release notes.
To run this sample, set up your MCP host (like Claude Desktop or VSCode Github Copilot Agents)
with the following configuration:
```json
{
"mcpServers": {
"sk_release_notes": {
"command": "uv",
"args": [
"--directory=<path to sk project>/semantic-kernel/python/samples/demos/mcp_server",
"run",
"mcp_server_with_prompts.py"
],
}
}
}
```
Note: You might need to set the uv to it's full path.
"""
template="""{{$messages}}
---
Group the following PRs into one of these buckets for release notes, keeping the same order:
-New Features
-Enhancements and Improvements
-Bug Fixes
-Python Package Updates
Include the output in raw markdown.
"""
@kernel_function(
name="run_prompt",
description="This run the prompts for a full set of release notes based on the PR messages given.",
)
asyncdefsampling_function(
messages: Annotated[str, "The list of PR messages, as a string with newlines"],
temperature: float=0.0,
max_tokens: int=1000,
# The include_in_function_choices is set to False, so it won't be included in the function choices,
# but it will get the server instance from the MCPPlugin that consumes this server.
server: Annotated[Server|None, "The server session", {"include_in_function_choices": False}] =None,
) ->str:
ifnotserver:
raiseValueError("Request context is required for sampling function.")
sampling_response=awaitserver.request_context.session.create_message(
messages=[
types.SamplingMessage(role="user", content=types.TextContent(type="text", text=messages)),
],
max_tokens=max_tokens,
temperature=temperature,
model_preferences=types.ModelPreferences(
hints=[types.ModelHint(name="gpt-4o-mini")],
),
)
logger.info(f"Sampling response: {sampling_response}")
returnsampling_response.content.text
defrun() ->None:
"""Run the MCP server with the release notes prompt template."""
kernel=Kernel()
kernel.add_function("release_notes", sampling_function)
prompt=KernelPromptTemplate(
prompt_template_config=PromptTemplateConfig(
name="release_notes_prompt",
description="This creates the prompts for a full set of release notes based on the PR messages given.",
template=template,
input_variables=[
InputVariable(
name="messages",
description="These are the PR messages, they are a single string with new lines.",
is_required=True,
json_schema='{"type": "string"}',
)
],
)
)
server=kernel.as_mcp_server(server_name="sk_release_notes", prompts=[prompt])
asyncdefhandle_stdin(stdin: Any|None=None, stdout: Any|None=None) ->None:
asyncwithstdio_server() as (read_stream, write_stream):
awaitserver.run(read_stream, write_stream, server.create_initialization_options())
anyio.run(handle_stdin)
if__name__=="__main__":
run()