-
Notifications
You must be signed in to change notification settings - Fork 21
Expand file tree
/
Copy pathREADME.html
More file actions
1518 lines (1495 loc) · 137 KB
/
README.html
File metadata and controls
1518 lines (1495 loc) · 137 KB
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
<meta charset="utf-8">
<meta name="generator" content="quarto-1.5.57">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>readme</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
vertical-align: middle;
}
/* CSS for syntax highlighting */
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { display: inline-block; text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
}
pre.numberSource { margin-left: 3em; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
</style>
<script src="README_files/libs/clipboard/clipboard.min.js"></script>
<script src="README_files/libs/quarto-html/quarto.js"></script>
<script src="README_files/libs/quarto-html/popper.min.js"></script>
<script src="README_files/libs/quarto-html/tippy.umd.min.js"></script>
<script src="README_files/libs/quarto-html/anchor.min.js"></script>
<link href="README_files/libs/quarto-html/tippy.css" rel="stylesheet">
<link href="README_files/libs/quarto-html/quarto-syntax-highlighting.css" rel="stylesheet" id="quarto-text-highlighting-styles">
<script src="README_files/libs/bootstrap/bootstrap.min.js"></script>
<link href="README_files/libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
<link href="README_files/libs/bootstrap/bootstrap.min.css" rel="stylesheet" id="quarto-bootstrap" data-mode="light">
</head>
<body class="fullcontent">
<div id="quarto-content" class="page-columns page-rows-contents page-layout-article">
<main class="content" id="quarto-document-content">
<section id="mcp2py-turn-any-mcp-server-into-a-python-module" class="level1">
<h1>mcp2py: Turn any MCP server into a python module</h1>
<p><strong>Use any MCP server in 3 lines of python code.</strong></p>
<div id="da24dfaa" class="cell" data-execution_count="1">
<div class="sourceCode cell-code" id="cb1"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"npx -y @modelcontextprotocol/server-filesystem /home"</span>)</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(server.list_directory(<span class="st">"."</span>)[:<span class="dv">100</span>])</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<pre><code>[DIR] .claude
[FILE] .coverage
[DIR] .git
[FILE] .gitignore
[DIR] .mypy_cache
[DIR] .pytest_cache
[F</code></pre>
<pre><code>ls tst</code></pre>
<p>That’s it. No configuration, no async/await, no setup. <strong>Just import, load, and call.</strong></p>
<hr>
<section id="quick-start" class="level2">
<h2 class="anchored" data-anchor-id="quick-start">Quick Start</h2>
<p><strong>1. Install</strong></p>
<div class="sourceCode" id="cb4"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ex">pip</span> install mcp2py</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p><strong>2. Use it</strong></p>
<div id="ef137eef" class="cell" data-execution_count="2">
<div class="sourceCode cell-code" id="cb5"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Load any MCP server</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"npx -y @modelcontextprotocol/server-filesystem ."</span>)</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="co"># Tools become {python} methods</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>files <span class="op">=</span> server.list_directory(path<span class="op">=</span><span class="st">"/tmp"</span>)</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>content <span class="op">=</span> server.read_file(path<span class="op">=</span><span class="st">"/tmp/test.txt"</span>)</span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>content</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p><strong>3. That’s it!</strong></p>
<p>The server runs as a subprocess, tools are {python} methods, everything just works. 9ce0cd5eccd4a6297f7cedddde808c40d3a59411999581a2f787c7237e7c6ccf —</p>
</section>
<section id="what-is-mcp" class="level2">
<h2 class="anchored" data-anchor-id="what-is-mcp">What is MCP?</h2>
<p>MCP servers expose <strong>tools</strong>, <strong>resources</strong>, and <strong>prompts</strong> via a protocol. mcp2py turns them into {python}:</p>
<ul>
<li>🔧 <strong>Tools</strong> → {python} functions</li>
<li>📦 <strong>Resources</strong> → {python} constants/attributes</li>
<li>📝 <strong>Prompts</strong> → Template functions/strings</li>
</ul>
</section>
<section id="philosophy" class="level2">
<h2 class="anchored" data-anchor-id="philosophy">Philosophy</h2>
<p><strong>It Just Works™ - But You Can Customize Everything</strong></p>
<p>mcp2py is designed for <strong>researchers, data analysts, and {python} beginners</strong> who want to try MCP servers without complexity. At the same time, it provides <strong>full control</strong> for developers building production applications.</p>
<p><strong>Zero configuration by default:</strong> - OAuth login? Browser opens automatically - Need user input? Terminal prompts appear - Server needs an LLM? We handle it - Everything “just works” out of the box</p>
<p><strong>No ceiling for advanced users:</strong> - Override any default behavior - Customize auth flows - Build production apps - Full control when you need it</p>
<p><strong>Your {python} REPL/code becomes an MCP client.</strong> The server is a separate process (Node.js, {python}, whatever) that mcp2py communicates with via JSON-RPC. Your {python} code can: - Call tools (server functions) as if they’re local {python} functions - Access resources (server data) as {python} attributes - Handle server requests (sampling, elicitation) automatically or via custom callbacks - Work seamlessly with any AI SDK (Anthropic, OpenAI, DSPy, etc.)</p>
</section>
<section id="getting-started" class="level2">
<h2 class="anchored" data-anchor-id="getting-started">Getting Started</h2>
<section id="for-beginners-researchers-it-just-works" class="level3">
<h3 class="anchored" data-anchor-id="for-beginners-researchers-it-just-works">For Beginners & Researchers: It Just Works</h3>
<div id="e5e837bf" class="cell" data-execution_count="3">
<div class="sourceCode cell-code" id="cb6"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Load any MCP server - that's it!</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"https://api.example.com/mcp"</span>)</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a><span class="co"># If it needs login:</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a><span class="co"># → Browser opens automatically</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a><span class="co"># → You log in once</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a><span class="co"># → Browser closes</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a><span class="co"># → Done!</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a><span class="co"># If it needs your input:</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a><span class="co"># → Nice terminal prompts appear</span></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a><span class="co"># → You answer</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a><span class="co"># → Code continues!</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a><span class="co"># If it needs AI help (sampling):</span></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a><span class="co"># → Uses your ANTHROPIC_API_KEY or OPENAI_API_KEY</span></span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a><span class="co"># → Handles it automatically</span></span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a><span class="co"># → You don't even notice!</span></span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a><span class="co"># Just use the tools!</span></span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a>result <span class="op">=</span> server.analyze_data(dataset<span class="op">=</span><span class="st">"sales_2024.csv"</span>)</span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(result)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p><strong>That’s it. No configuration. No setup. It just works.</strong></p>
<hr>
</section>
</section>
<section id="interface-design" class="level2">
<h2 class="anchored" data-anchor-id="interface-design">Interface Design</h2>
<section id="basic-usage" class="level3">
<h3 class="anchored" data-anchor-id="basic-usage">Basic Usage</h3>
<div id="2e917fc3" class="cell" data-execution_count="4">
<div class="sourceCode cell-code" id="cb7"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Load an MCP server - simple and clean</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>weather <span class="op">=</span> load(<span class="st">"npx -y @h1deya/mcp-server-weather"</span>)</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="co"># Or from a remote HTTP server (SSE/HTTP Stream transport)</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>api <span class="op">=</span> load(<span class="st">"https://api.example.com/mcp"</span>)</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="co"># With authentication</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>api <span class="op">=</span> load(<span class="st">"https://api.example.com/mcp"</span>, headers<span class="op">=</span>{<span class="st">"Authorization"</span>: <span class="st">"Bearer YOUR_TOKEN"</span>})</span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a><span class="co"># Or from a {python} script</span></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a>travel <span class="op">=</span> load(<span class="st">"</span><span class="sc">{python}</span><span class="st"> my_mcp_server.py"</span>)</span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a><span class="co"># Tools become functions</span></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a>alerts <span class="op">=</span> weather.get_alerts(state<span class="op">=</span><span class="st">"CA"</span>)</span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a>forecast <span class="op">=</span> weather.get_forecast(latitude<span class="op">=</span><span class="fl">37.7749</span>, longitude<span class="op">=-</span><span class="fl">122.4194</span>)</span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(forecast)</span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a><span class="co"># Resources become attributes</span></span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(weather.API_DOCUMENTATION) <span class="co"># Constant resource</span></span>
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(weather.current_config) <span class="co"># Dynamic resource</span></span>
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true" tabindex="-1"></a><span class="co"># Prompts become template functions</span></span>
<span id="cb7-25"><a href="#cb7-25" aria-hidden="true" tabindex="-1"></a>prompt <span class="op">=</span> weather.create_weather_report(location<span class="op">=</span><span class="st">"NYC"</span>, style<span class="op">=</span><span class="st">"casual"</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="use-with-ai-frameworks-dspy-claudette-etc." class="level3">
<h3 class="anchored" data-anchor-id="use-with-ai-frameworks-dspy-claudette-etc.">Use with AI Frameworks (DSPy, Claudette, etc.)</h3>
<p><strong>The <code>.tools</code> attribute gives you a list of callable {python} functions</strong>:</p>
<div id="1c47d690" class="cell" data-execution_count="5">
<div class="sourceCode cell-code" id="cb8"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"npx -y @modelcontextprotocol/server-filesystem /tmp"</span>)</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a><span class="co"># Get tools as callable functions</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>tools <span class="op">=</span> server.tools</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a><span class="co"># [<function read_file>, <function write_file>, ...]</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a><span class="co"># Each function has __name__ and __doc__</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(tools[<span class="dv">0</span>].<span class="va">__name__</span>) <span class="co"># "read_file"</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(tools[<span class="dv">0</span>].__doc__) <span class="co"># "Read a file from the filesystem"</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a><span class="co"># And they're callable!</span></span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a>result <span class="op">=</span> tools[<span class="dv">0</span>](path<span class="op">=</span><span class="st">"/tmp/test.txt"</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="working-with-ai-frameworks" class="level3">
<h3 class="anchored" data-anchor-id="working-with-ai-frameworks">Working with AI Frameworks</h3>
<p>The <code>.tools</code> attribute gives you callable functions ready for frameworks like DSPy and Claudette:</p>
<div id="f849ad83" class="cell" data-execution_count="6">
<div class="sourceCode cell-code" id="cb9"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> dspy</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="co"># Load MCP server</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>travel <span class="op">=</span> load(<span class="st">"</span><span class="sc">{python}</span><span class="st"> airline_server.py"</span>)</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a><span class="co"># Use with DSPy - pass callable functions directly</span></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> CustomerService(dspy.Signature):</span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a> user_request: <span class="bu">str</span> <span class="op">=</span> dspy.InputField()</span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a> result: <span class="bu">str</span> <span class="op">=</span> dspy.OutputField()</span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a>dspy.configure(lm<span class="op">=</span>dspy.LM(<span class="st">"openai/gpt-4o-mini"</span>))</span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a><span class="co"># Pass tools directly to DSPy (it expects callables)</span></span>
<span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a>react <span class="op">=</span> dspy.ReAct(CustomerService, tools<span class="op">=</span>travel.tools)</span>
<span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-17"><a href="#cb9-17" aria-hidden="true" tabindex="-1"></a>result <span class="op">=</span> react(user_request<span class="op">=</span><span class="st">"Book a flight from SFO to JFK on 09/01/2025"</span>)</span>
<span id="cb9-18"><a href="#cb9-18" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(result)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<div id="6c83cf86" class="cell" data-execution_count="7">
<div class="sourceCode cell-code" id="cb10"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Also works with Claudette</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> claudette <span class="im">import</span> Chat</span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>weather <span class="op">=</span> load(<span class="st">"npx -y @h1deya/mcp-server-weather"</span>)</span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a><span class="co"># Claudette expects callable functions</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>chat <span class="op">=</span> Chat(model<span class="op">=</span><span class="st">"claude-3-5-sonnet-20241022"</span>, tools<span class="op">=</span>weather.tools)</span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a>response <span class="op">=</span> chat(<span class="st">"What's the weather in Tokyo?"</span>)</span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a><span class="co"># Claudette automatically calls the tools as needed</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(response)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p><strong>Note:</strong> For SDKs that have native MCP support (Anthropic, OpenAI, Google Gemini), use their built-in MCP integration directly. The <code>.tools</code> attribute is for frameworks like DSPy and Claudette that expect {python} callables.</p>
</section>
<section id="type-safety-ide-support" class="level3">
<h3 class="anchored" data-anchor-id="type-safety-ide-support">Type Safety & IDE Support</h3>
<p><strong>Auto-generated stubs for perfect autocomplete:</strong></p>
<div id="3470245c" class="cell" data-execution_count="8">
<div class="sourceCode cell-code" id="cb11"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Stubs auto-generated to ~/.cache/mcp2py/stubs/</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"npx my-server"</span>)</span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a><span class="co"># IDE now has full autocomplete and type hints!</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a>server.search_files(</span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a> pattern<span class="op">=</span><span class="st">"*.py"</span>, <span class="co"># type: str - IDE knows this!</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a> max_results<span class="op">=</span><span class="dv">10</span> <span class="co"># type: int, optional - IDE suggests this!</span></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a>) <span class="co"># Returns: dict[str, Any] - IDE shows return type!</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p><strong>Manual stub generation:</strong></p>
<div id="75ddfda9" class="cell" data-execution_count="9">
<div class="sourceCode cell-code" id="cb12"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Generate stub to specific location for your project</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"npx weather-server"</span>)</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>server.generate_stubs(<span class="st">"./stubs/weather.pyi"</span>)</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a><span class="co"># Or let it auto-cache (default behavior)</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a><span class="co"># Stubs saved to: ~/.cache/mcp2py/stubs/<command_hash>.pyi</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p><strong>How it works:</strong> - <code>load()</code> returns a <strong>dynamically typed class</strong> with all methods pre-defined - Your IDE sees proper type hints immediately - <strong>no configuration needed!</strong> - Type hints include parameter names, types, defaults, and return types - Works in VS Code, PyCharm, Jupyter notebooks, and any {python} IDE - Also generates <code>.pyi</code> stub files to <code>~/.cache/mcp2py/stubs/</code> for reference</p>
<p><strong>Zero configuration required</strong> - autocomplete just works! ✨</p>
</section>
</section>
<section id="mcp-client-features" class="level2">
<h2 class="anchored" data-anchor-id="mcp-client-features">MCP Client Features</h2>
<p>When your {python} code acts as an MCP client, servers may request these capabilities:</p>
<section id="sampling" class="level3">
<h3 class="anchored" data-anchor-id="sampling"><strong>Sampling</strong></h3>
<p>When a server needs LLM completions, mcp2py handles it automatically.</p>
<p><strong>Default: Works Out of the Box</strong></p>
<div id="1b463ad9" class="cell" data-execution_count="10">
<div class="sourceCode cell-code" id="cb13"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Just works! Uses your default LLM</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"npx travel-server"</span>)</span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a><span class="co"># If server needs LLM help, mcp2py:</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a><span class="co"># 1. Checks for ANTHROPIC_API_KEY or OPENAI_API_KEY in environment</span></span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a><span class="co"># 2. Calls the LLM automatically</span></span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a><span class="co"># 3. Returns result to server</span></span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a><span class="co"># 4. Your code continues!</span></span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true" tabindex="-1"></a>result <span class="op">=</span> server.book_flight(destination<span class="op">=</span><span class="st">"Tokyo"</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p><strong>Configure your preferred LLM:</strong></p>
<div id="e50f548b" class="cell" data-execution_count="11">
<div class="sourceCode cell-code" id="cb14"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Set via environment (recommended)</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> os</span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>os.environ[<span class="st">"ANTHROPIC_API_KEY"</span>] <span class="op">=</span> <span class="st">"sk-..."</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a><span class="co"># Or configure globally using LiteLLM model strings</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> configure</span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a>configure(</span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a> model<span class="op">=</span><span class="st">"claude-3-5-sonnet-20241022"</span> <span class="co"># or "gpt-4o", "gemini/gemini-pro", etc.</span></span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a><span class="co"># LiteLLM automatically detects the right API based on model name</span></span>
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a><span class="co"># Uses standard env vars: ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.</span></span>
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-15"><a href="#cb14-15" aria-hidden="true" tabindex="-1"></a><span class="co"># Now all servers use this LLM for sampling</span></span>
<span id="cb14-16"><a href="#cb14-16" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"npx travel-server"</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p><strong>Advanced: Custom Sampling Handler</strong></p>
<div id="f511fa92" class="cell" data-execution_count="12">
<div class="sourceCode cell-code" id="cb15"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> my_sampling_handler(messages, model_prefs, system_prompt, max_tokens):</span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Full control over LLM calls."""</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a> <span class="im">import</span> anthropic</span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a> client <span class="op">=</span> anthropic.Anthropic()</span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a> response <span class="op">=</span> client.messages.create(</span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a> model<span class="op">=</span><span class="st">"claude-3-5-sonnet-20241022"</span>,</span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a> messages<span class="op">=</span>messages,</span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true" tabindex="-1"></a> max_tokens<span class="op">=</span>max_tokens</span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> response.content[<span class="dv">0</span>].text</span>
<span id="cb15-13"><a href="#cb15-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-14"><a href="#cb15-14" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(</span>
<span id="cb15-15"><a href="#cb15-15" aria-hidden="true" tabindex="-1"></a> <span class="st">"npx travel-server"</span>,</span>
<span id="cb15-16"><a href="#cb15-16" aria-hidden="true" tabindex="-1"></a> on_sampling<span class="op">=</span>my_sampling_handler <span class="co"># Override default</span></span>
<span id="cb15-17"><a href="#cb15-17" aria-hidden="true" tabindex="-1"></a>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p><strong>Disable sampling (for security/cost control):</strong></p>
<div id="e5ded5fb" class="cell" data-execution_count="13">
<div class="sourceCode cell-code" id="cb16"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a> <span class="st">"npx travel-server"</span>,</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a> allow_sampling<span class="op">=</span><span class="va">False</span> <span class="co"># Raises error if server requests LLM</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="elicitation" class="level3">
<h3 class="anchored" data-anchor-id="elicitation"><strong>Elicitation</strong></h3>
<p>When a server needs user input, mcp2py prompts automatically.</p>
<p><strong>Default: Terminal Prompts</strong></p>
<div id="ef0dc379" class="cell" data-execution_count="14">
<div class="sourceCode cell-code" id="cb17"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Just works! Terminal prompts appear automatically</span></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"npx travel-server"</span>)</span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a><span class="co"># Server asks: "Confirm booking for $500?"</span></span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a><span class="co"># Terminal shows:</span></span>
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span>
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true" tabindex="-1"></a><span class="co"># Server asks: Confirm booking for $500?</span></span>
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true" tabindex="-1"></a><span class="co"># confirm_booking (boolean): y/n</span></span>
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span>
<span id="cb17-12"><a href="#cb17-12" aria-hidden="true" tabindex="-1"></a><span class="co"># You type: y</span></span>
<span id="cb17-13"><a href="#cb17-13" aria-hidden="true" tabindex="-1"></a><span class="co"># Code continues!</span></span>
<span id="cb17-14"><a href="#cb17-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-15"><a href="#cb17-15" aria-hidden="true" tabindex="-1"></a>result <span class="op">=</span> server.book_flight(destination<span class="op">=</span><span class="st">"Paris"</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p><strong>What you see:</strong></p>
<pre><code>Calling book_flight...
┌─────────────────────────────────────────┐
│ 🔔 Server needs your input │
├─────────────────────────────────────────┤
│ Confirm booking for $500? │
│ │
│ confirm_booking (boolean): y/n │
│ seat_preference (window/aisle/middle): │
│ meal_preference (optional): │
└─────────────────────────────────────────┘
> y
> window
> vegetarian
Booking confirmed!</code></pre>
<p><strong>Advanced: Custom Elicitation Handler</strong></p>
<div id="c2e6bb06" class="cell" data-execution_count="15">
<div class="sourceCode cell-code" id="cb19"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> my_input_handler(message, schema):</span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Custom UI for user input."""</span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a> <span class="co"># Build a GUI, web form, voice input, etc.</span></span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a> <span class="im">from</span> tkinter <span class="im">import</span> simpledialog</span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> simpledialog.askstring(<span class="st">"Server Request"</span>, message)</span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(</span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a> <span class="st">"npx travel-server"</span>,</span>
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a> on_elicitation<span class="op">=</span>my_input_handler</span>
<span id="cb19-12"><a href="#cb19-12" aria-hidden="true" tabindex="-1"></a>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p><strong>Disable elicitation (for automated scripts):</strong></p>
<div id="cdd97ca0" class="cell" data-execution_count="16">
<div class="sourceCode cell-code" id="cb20"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a> <span class="st">"npx travel-server"</span>,</span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a> allow_elicitation<span class="op">=</span><span class="va">False</span> <span class="co"># Raises error if server asks for input</span></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a><span class="co"># Or provide pre-filled answers</span></span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(</span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a> <span class="st">"npx travel-server"</span>,</span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a> elicitation_defaults<span class="op">=</span>{</span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a> <span class="st">"confirm_booking"</span>: <span class="va">True</span>,</span>
<span id="cb20-11"><a href="#cb20-11" aria-hidden="true" tabindex="-1"></a> <span class="st">"seat_preference"</span>: <span class="st">"window"</span></span>
<span id="cb20-12"><a href="#cb20-12" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb20-13"><a href="#cb20-13" aria-hidden="true" tabindex="-1"></a>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="roots" class="level3">
<h3 class="anchored" data-anchor-id="roots"><strong>Roots</strong></h3>
<p>Servers can ask which directories to focus on. Optional, simple:</p>
<div id="6619772b" class="cell" data-execution_count="17">
<div class="sourceCode cell-code" id="cb21"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Single directory</span></span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"npx filesystem-server"</span>, roots<span class="op">=</span><span class="st">"/home/user/projects"</span>)</span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a><span class="co"># Multiple directories</span></span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(</span>
<span id="cb21-6"><a href="#cb21-6" aria-hidden="true" tabindex="-1"></a> <span class="st">"npx filesystem-server"</span>,</span>
<span id="cb21-7"><a href="#cb21-7" aria-hidden="true" tabindex="-1"></a> roots<span class="op">=</span>[<span class="st">"/home/user/projects"</span>, <span class="st">"/tmp/workspace"</span>]</span>
<span id="cb21-8"><a href="#cb21-8" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb21-9"><a href="#cb21-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb21-10"><a href="#cb21-10" aria-hidden="true" tabindex="-1"></a><span class="co"># Update roots dynamically</span></span>
<span id="cb21-11"><a href="#cb21-11" aria-hidden="true" tabindex="-1"></a>server.set_roots([<span class="st">"/home/user/new-project"</span>])</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
</section>
<section id="design-rules" class="level2">
<h2 class="anchored" data-anchor-id="design-rules">Design Rules</h2>
<section id="tools-functions" class="level3">
<h3 class="anchored" data-anchor-id="tools-functions">1. <strong>Tools → Functions</strong></h3>
<p>MCP tools map to {python} functions with full support for:</p>
<ul>
<li><strong>Arguments</strong>: Both required and optional parameters</li>
<li><strong>Type hints</strong>: Generated from JSON Schema <code>inputSchema</code></li>
<li><strong>Docstrings</strong>: Built from tool <code>description</code></li>
<li><strong>Return types</strong>: Typed as <code>dict[str, Any]</code> (MCP tools return JSON)</li>
</ul>
<p><strong>Naming convention</strong>: Snake_case (MCP <code>getWeather</code> → {python} <code>get_weather</code>)</p>
<div id="cdf77a5a" class="cell" data-execution_count="18">
<div class="sourceCode cell-code" id="cb22"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="co"># MCP Tool Definition:</span></span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a><span class="co"># {</span></span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a><span class="co"># "name": "searchFiles",</span></span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a><span class="co"># "description": "Search for files matching a pattern",</span></span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a><span class="co"># "inputSchema": {</span></span>
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a><span class="co"># "type": "object",</span></span>
<span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a><span class="co"># "properties": {</span></span>
<span id="cb22-8"><a href="#cb22-8" aria-hidden="true" tabindex="-1"></a><span class="co"># "pattern": {"type": "string", "description": "Glob pattern"},</span></span>
<span id="cb22-9"><a href="#cb22-9" aria-hidden="true" tabindex="-1"></a><span class="co"># "maxResults": {"type": "integer", "default": 100}</span></span>
<span id="cb22-10"><a href="#cb22-10" aria-hidden="true" tabindex="-1"></a><span class="co"># },</span></span>
<span id="cb22-11"><a href="#cb22-11" aria-hidden="true" tabindex="-1"></a><span class="co"># "required": ["pattern"]</span></span>
<span id="cb22-12"><a href="#cb22-12" aria-hidden="true" tabindex="-1"></a><span class="co"># }</span></span>
<span id="cb22-13"><a href="#cb22-13" aria-hidden="true" tabindex="-1"></a><span class="co"># }</span></span>
<span id="cb22-14"><a href="#cb22-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-15"><a href="#cb22-15" aria-hidden="true" tabindex="-1"></a><span class="co"># Generated {python}:</span></span>
<span id="cb22-16"><a href="#cb22-16" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> search_files(pattern: <span class="bu">str</span>, max_results: <span class="bu">int</span> <span class="op">=</span> <span class="dv">100</span>) <span class="op">-></span> <span class="bu">dict</span>[<span class="bu">str</span>, Any]:</span>
<span id="cb22-17"><a href="#cb22-17" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Search for files matching a pattern.</span></span>
<span id="cb22-18"><a href="#cb22-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-19"><a href="#cb22-19" aria-hidden="true" tabindex="-1"></a><span class="co"> Args:</span></span>
<span id="cb22-20"><a href="#cb22-20" aria-hidden="true" tabindex="-1"></a><span class="co"> pattern: Glob pattern</span></span>
<span id="cb22-21"><a href="#cb22-21" aria-hidden="true" tabindex="-1"></a><span class="co"> max_results: Maximum results to return (default: 100)</span></span>
<span id="cb22-22"><a href="#cb22-22" aria-hidden="true" tabindex="-1"></a><span class="co"> """</span></span>
<span id="cb22-23"><a href="#cb22-23" aria-hidden="true" tabindex="-1"></a> ...</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="resources-constants-or-properties" class="level3">
<h3 class="anchored" data-anchor-id="resources-constants-or-properties">2. <strong>Resources → Constants or Properties</strong></h3>
<p>Resources map differently based on their nature:</p>
<ul>
<li><strong>Static resources</strong> (like documentation, schemas): Module-level constants (UPPER_CASE)</li>
<li><strong>Dynamic resources</strong> (may change): Properties with getters (lowercase)</li>
</ul>
<div id="58579601" class="cell" data-execution_count="19">
<div class="sourceCode cell-code" id="cb23"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Static resource (cached)</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a>API_DOCS: <span class="bu">str</span> <span class="op">=</span> server._get_resource(<span class="st">"api://docs"</span>)</span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a><span class="co"># Dynamic resource (fetched on access)</span></span>
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a><span class="at">@property</span></span>
<span id="cb23-6"><a href="#cb23-6" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> current_status() <span class="op">-></span> <span class="bu">dict</span>[<span class="bu">str</span>, Any]:</span>
<span id="cb23-7"><a href="#cb23-7" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Current server status."""</span></span>
<span id="cb23-8"><a href="#cb23-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> server._get_resource(<span class="st">"status://current"</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p><strong>Naming convention</strong>: - Static: <code>UPPER_SNAKE_CASE</code> - Dynamic: <code>lower_snake_case</code> properties</p>
</section>
<section id="prompts-template-functions" class="level3">
<h3 class="anchored" data-anchor-id="prompts-template-functions">3. <strong>Prompts → Template Functions</strong></h3>
<p>Prompts become functions that return formatted strings:</p>
<div id="976b8b5d" class="cell" data-execution_count="20">
<div class="sourceCode cell-code" id="cb24"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="co"># MCP Prompt:</span></span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a><span class="co"># {</span></span>
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a><span class="co"># "name": "reviewCode",</span></span>
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a><span class="co"># "description": "Generate a code review prompt",</span></span>
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a><span class="co"># "arguments": [</span></span>
<span id="cb24-6"><a href="#cb24-6" aria-hidden="true" tabindex="-1"></a><span class="co"># {"name": "code", "description": "Code to review", "required": true},</span></span>
<span id="cb24-7"><a href="#cb24-7" aria-hidden="true" tabindex="-1"></a><span class="co"># {"name": "focus", "description": "Review focus area", "required": false}</span></span>
<span id="cb24-8"><a href="#cb24-8" aria-hidden="true" tabindex="-1"></a><span class="co"># ]</span></span>
<span id="cb24-9"><a href="#cb24-9" aria-hidden="true" tabindex="-1"></a><span class="co"># }</span></span>
<span id="cb24-10"><a href="#cb24-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb24-11"><a href="#cb24-11" aria-hidden="true" tabindex="-1"></a><span class="co"># Generated {python}:</span></span>
<span id="cb24-12"><a href="#cb24-12" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> review_code(code: <span class="bu">str</span>, focus: <span class="bu">str</span> <span class="op">|</span> <span class="va">None</span> <span class="op">=</span> <span class="va">None</span>) <span class="op">-></span> <span class="bu">str</span>:</span>
<span id="cb24-13"><a href="#cb24-13" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Generate a code review prompt.</span></span>
<span id="cb24-14"><a href="#cb24-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb24-15"><a href="#cb24-15" aria-hidden="true" tabindex="-1"></a><span class="co"> Args:</span></span>
<span id="cb24-16"><a href="#cb24-16" aria-hidden="true" tabindex="-1"></a><span class="co"> code: Code to review</span></span>
<span id="cb24-17"><a href="#cb24-17" aria-hidden="true" tabindex="-1"></a><span class="co"> focus: Review focus area (optional)</span></span>
<span id="cb24-18"><a href="#cb24-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb24-19"><a href="#cb24-19" aria-hidden="true" tabindex="-1"></a><span class="co"> Returns:</span></span>
<span id="cb24-20"><a href="#cb24-20" aria-hidden="true" tabindex="-1"></a><span class="co"> Formatted prompt string ready for LLM</span></span>
<span id="cb24-21"><a href="#cb24-21" aria-hidden="true" tabindex="-1"></a><span class="co"> """</span></span>
<span id="cb24-22"><a href="#cb24-22" aria-hidden="true" tabindex="-1"></a> ...</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="error-handling" class="level3">
<h3 class="anchored" data-anchor-id="error-handling">4. <strong>Error Handling</strong></h3>
<p>{python}ic exceptions for common failures:</p>
<div id="995345a2" class="cell" data-execution_count="21">
<div class="sourceCode cell-code" id="cb25"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py.exceptions <span class="im">import</span> (</span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a> MCPConnectionError, <span class="co"># Can't connect to server</span></span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a> MCPToolError, <span class="co"># Tool execution failed</span></span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a> MCPResourceError, <span class="co"># Resource not found</span></span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a> MCPValidationError, <span class="co"># Invalid arguments</span></span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-8"><a href="#cb25-8" aria-hidden="true" tabindex="-1"></a><span class="cf">try</span>:</span>
<span id="cb25-9"><a href="#cb25-9" aria-hidden="true" tabindex="-1"></a> result <span class="op">=</span> server.expensive_operation(data<span class="op">=</span>large_data)</span>
<span id="cb25-10"><a href="#cb25-10" aria-hidden="true" tabindex="-1"></a><span class="cf">except</span> MCPValidationError <span class="im">as</span> e:</span>
<span id="cb25-11"><a href="#cb25-11" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Invalid input: </span><span class="sc">{</span>e<span class="sc">}</span><span class="ss">"</span>)</span>
<span id="cb25-12"><a href="#cb25-12" aria-hidden="true" tabindex="-1"></a><span class="cf">except</span> MCPToolError <span class="im">as</span> e:</span>
<span id="cb25-13"><a href="#cb25-13" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Tool failed: </span><span class="sc">{</span>e<span class="sc">}</span><span class="ss">"</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="async-support" class="level3">
<h3 class="anchored" data-anchor-id="async-support">5. <strong>Async Support</strong></h3>
<p>Use <code>aload()</code> for async MCP servers:</p>
<div id="56cbc82e" class="cell" data-execution_count="22">
<div class="sourceCode cell-code" id="cb26"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> aload</span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Async version - all tools become async</span></span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> <span class="cf">await</span> aload(<span class="st">"npx async-server"</span>)</span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a>result <span class="op">=</span> <span class="cf">await</span> server.fetch_data(url<span class="op">=</span><span class="st">"https://example.com"</span>)</span>
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true" tabindex="-1"></a>status <span class="op">=</span> <span class="cf">await</span> server.get_current_status()</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="context-managers" class="level3">
<h3 class="anchored" data-anchor-id="context-managers">6. <strong>Context Managers</strong></h3>
<p>Automatic cleanup when using <code>with</code>:</p>
<div id="7ff0f87e" class="cell" data-execution_count="23">
<div class="sourceCode cell-code" id="cb27"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Sync version</span></span>
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a><span class="cf">with</span> load(<span class="st">"npx my-server"</span>) <span class="im">as</span> server:</span>
<span id="cb27-5"><a href="#cb27-5" aria-hidden="true" tabindex="-1"></a> result <span class="op">=</span> server.do_work()</span>
<span id="cb27-6"><a href="#cb27-6" aria-hidden="true" tabindex="-1"></a><span class="co"># Server process automatically terminated</span></span>
<span id="cb27-7"><a href="#cb27-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb27-8"><a href="#cb27-8" aria-hidden="true" tabindex="-1"></a><span class="co"># Async version</span></span>
<span id="cb27-9"><a href="#cb27-9" aria-hidden="true" tabindex="-1"></a><span class="cf">async</span> <span class="cf">with</span> aload(<span class="st">"npx my-server"</span>) <span class="im">as</span> server:</span>
<span id="cb27-10"><a href="#cb27-10" aria-hidden="true" tabindex="-1"></a> result <span class="op">=</span> <span class="cf">await</span> server.do_work()</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
</section>
<section id="configuration" class="level2">
<h2 class="anchored" data-anchor-id="configuration">Configuration</h2>
<section id="server-registry-optional" class="level3">
<h3 class="anchored" data-anchor-id="server-registry-optional">Server Registry (Optional)</h3>
<p>Register commonly-used servers once, then load by name:</p>
<div id="f85ba108" class="cell" data-execution_count="24">
<div class="sourceCode cell-code" id="cb28"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> register, load</span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Register servers (run once, e.g., in your setup script)</span></span>
<span id="cb28-4"><a href="#cb28-4" aria-hidden="true" tabindex="-1"></a>register(</span>
<span id="cb28-5"><a href="#cb28-5" aria-hidden="true" tabindex="-1"></a> weather<span class="op">=</span><span class="st">"npx -y @h1deya/mcp-server-weather"</span>,</span>
<span id="cb28-6"><a href="#cb28-6" aria-hidden="true" tabindex="-1"></a> brave<span class="op">=</span><span class="st">"npx -y brave-search-mcp-server"</span>,</span>
<span id="cb28-7"><a href="#cb28-7" aria-hidden="true" tabindex="-1"></a> filesystem<span class="op">=</span><span class="st">"npx -y @modelcontextprotocol/server-filesystem /tmp"</span>,</span>
<span id="cb28-8"><a href="#cb28-8" aria-hidden="true" tabindex="-1"></a> myserver<span class="op">=</span><span class="st">"</span><span class="sc">{python}</span><span class="st"> my_mcp_server.py"</span></span>
<span id="cb28-9"><a href="#cb28-9" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb28-10"><a href="#cb28-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-11"><a href="#cb28-11" aria-hidden="true" tabindex="-1"></a><span class="co"># Then load by name anywhere</span></span>
<span id="cb28-12"><a href="#cb28-12" aria-hidden="true" tabindex="-1"></a>weather <span class="op">=</span> load(<span class="st">"weather"</span>)</span>
<span id="cb28-13"><a href="#cb28-13" aria-hidden="true" tabindex="-1"></a>brave <span class="op">=</span> load(<span class="st">"brave"</span>)</span>
<span id="cb28-14"><a href="#cb28-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-15"><a href="#cb28-15" aria-hidden="true" tabindex="-1"></a><span class="co"># Or use commands directly (no registration needed)</span></span>
<span id="cb28-16"><a href="#cb28-16" aria-hidden="true" tabindex="-1"></a>custom <span class="op">=</span> load(<span class="st">"npx my-custom-server"</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p>Registry is saved to <code>~/.config/mcp2py/servers.json</code> automatically.</p>
</section>
<section id="remote-servers-authentication" class="level3">
<h3 class="anchored" data-anchor-id="remote-servers-authentication">Remote Servers & Authentication</h3>
<p>MCP servers can be hosted remotely over HTTP (using SSE or HTTP Stream transport):</p>
<div id="b9779e5e" class="cell" data-execution_count="25">
<div class="sourceCode cell-code" id="cb29"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load, register</span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Connect to remote MCP server</span></span>
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a>api <span class="op">=</span> load(<span class="st">"https://api.example.com/mcp"</span>)</span>
<span id="cb29-5"><a href="#cb29-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb29-6"><a href="#cb29-6" aria-hidden="true" tabindex="-1"></a><span class="co"># With Bearer token authentication</span></span>
<span id="cb29-7"><a href="#cb29-7" aria-hidden="true" tabindex="-1"></a>secure_api <span class="op">=</span> load(</span>
<span id="cb29-8"><a href="#cb29-8" aria-hidden="true" tabindex="-1"></a> <span class="st">"https://api.example.com/mcp"</span>,</span>
<span id="cb29-9"><a href="#cb29-9" aria-hidden="true" tabindex="-1"></a> headers<span class="op">=</span>{<span class="st">"Authorization"</span>: <span class="st">"Bearer sk-1234567890"</span>}</span>
<span id="cb29-10"><a href="#cb29-10" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb29-11"><a href="#cb29-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb29-12"><a href="#cb29-12" aria-hidden="true" tabindex="-1"></a><span class="co"># With custom headers (API keys, etc.)</span></span>
<span id="cb29-13"><a href="#cb29-13" aria-hidden="true" tabindex="-1"></a>custom_api <span class="op">=</span> load(</span>
<span id="cb29-14"><a href="#cb29-14" aria-hidden="true" tabindex="-1"></a> <span class="st">"https://api.example.com/mcp"</span>,</span>
<span id="cb29-15"><a href="#cb29-15" aria-hidden="true" tabindex="-1"></a> headers<span class="op">=</span>{</span>
<span id="cb29-16"><a href="#cb29-16" aria-hidden="true" tabindex="-1"></a> <span class="st">"X-API-Key"</span>: <span class="st">"your-api-key"</span>,</span>
<span id="cb29-17"><a href="#cb29-17" aria-hidden="true" tabindex="-1"></a> <span class="st">"X-Client-ID"</span>: <span class="st">"your-client-id"</span></span>
<span id="cb29-18"><a href="#cb29-18" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb29-19"><a href="#cb29-19" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb29-20"><a href="#cb29-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb29-21"><a href="#cb29-21" aria-hidden="true" tabindex="-1"></a><span class="co"># Register remote servers too</span></span>
<span id="cb29-22"><a href="#cb29-22" aria-hidden="true" tabindex="-1"></a>register(</span>
<span id="cb29-23"><a href="#cb29-23" aria-hidden="true" tabindex="-1"></a> production_api<span class="op">=</span><span class="st">"https://api.prod.example.com/mcp"</span>,</span>
<span id="cb29-24"><a href="#cb29-24" aria-hidden="true" tabindex="-1"></a> staging_api<span class="op">=</span><span class="st">"https://api.staging.example.com/mcp"</span></span>
<span id="cb29-25"><a href="#cb29-25" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb29-26"><a href="#cb29-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb29-27"><a href="#cb29-27" aria-hidden="true" tabindex="-1"></a><span class="co"># Load with auth at runtime</span></span>
<span id="cb29-28"><a href="#cb29-28" aria-hidden="true" tabindex="-1"></a>prod <span class="op">=</span> load(<span class="st">"production_api"</span>, headers<span class="op">=</span>{<span class="st">"Authorization"</span>: <span class="ss">f"Bearer </span><span class="sc">{</span>get_token()<span class="sc">}</span><span class="ss">"</span>})</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p><strong>Use cases for remote MCP servers:</strong> - Company-hosted internal tools - Paid API services via MCP - Shared team resources (databases, analytics, etc.) - Cloud-based AI tool marketplaces</p>
</section>
<section id="oauth-authentication-google-github-etc." class="level3">
<h3 class="anchored" data-anchor-id="oauth-authentication-google-github-etc.">OAuth Authentication (Google, GitHub, etc.)</h3>
<p><strong>Default: Zero Configuration (For beginners, researchers, data analysts)</strong></p>
<p>mcp2py handles OAuth automatically - just load and go:</p>
<div id="5467f645" class="cell" data-execution_count="26">
<div class="sourceCode cell-code" id="cb30"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-3"><a href="#cb30-3" aria-hidden="true" tabindex="-1"></a><span class="co"># That's it! Browser opens, you log in, then continue coding</span></span>
<span id="cb30-4"><a href="#cb30-4" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"https://api.example.com/mcp"</span>)</span>
<span id="cb30-5"><a href="#cb30-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-6"><a href="#cb30-6" aria-hidden="true" tabindex="-1"></a><span class="co"># First tool call triggers OAuth if needed:</span></span>
<span id="cb30-7"><a href="#cb30-7" aria-hidden="true" tabindex="-1"></a><span class="co"># 1. Browser window pops up</span></span>
<span id="cb30-8"><a href="#cb30-8" aria-hidden="true" tabindex="-1"></a><span class="co"># 2. You log in (Google/GitHub/etc.)</span></span>
<span id="cb30-9"><a href="#cb30-9" aria-hidden="true" tabindex="-1"></a><span class="co"># 3. Window closes automatically</span></span>
<span id="cb30-10"><a href="#cb30-10" aria-hidden="true" tabindex="-1"></a><span class="co"># 4. Your code continues!</span></span>
<span id="cb30-11"><a href="#cb30-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-12"><a href="#cb30-12" aria-hidden="true" tabindex="-1"></a>result <span class="op">=</span> server.my_tool() <span class="co"># Works immediately after login</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p><strong>What happens under the hood:</strong> - mcp2py detects OAuth requirement (401 response) - Discovers OAuth endpoints automatically - Opens browser for login (PKCE-secured) - Stores tokens in <code>~/.config/mcp2py/tokens.json</code> - Refreshes tokens automatically when they expire</p>
<p><strong>You never think about tokens.</strong></p>
<hr>
<p><strong>Advanced: Custom OAuth (For production apps)</strong></p>
<p>Override defaults when building applications:</p>
<div id="578dc858" class="cell" data-execution_count="27">
<div class="sourceCode cell-code" id="cb31"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Option 1: Custom token provider</span></span>
<span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> get_google_token():</span>
<span id="cb31-5"><a href="#cb31-5" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Your custom OAuth logic."""</span></span>
<span id="cb31-6"><a href="#cb31-6" aria-hidden="true" tabindex="-1"></a> <span class="im">from</span> google.oauth2.credentials <span class="im">import</span> Credentials</span>
<span id="cb31-7"><a href="#cb31-7" aria-hidden="true" tabindex="-1"></a> <span class="co"># Your implementation here</span></span>
<span id="cb31-8"><a href="#cb31-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> creds.token</span>
<span id="cb31-9"><a href="#cb31-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-10"><a href="#cb31-10" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(</span>
<span id="cb31-11"><a href="#cb31-11" aria-hidden="true" tabindex="-1"></a> <span class="st">"https://api.example.com/mcp"</span>,</span>
<span id="cb31-12"><a href="#cb31-12" aria-hidden="true" tabindex="-1"></a> auth<span class="op">=</span>get_google_token <span class="co"># Called when token needed</span></span>
<span id="cb31-13"><a href="#cb31-13" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb31-14"><a href="#cb31-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-15"><a href="#cb31-15" aria-hidden="true" tabindex="-1"></a><span class="co"># Option 2: Service account (no browser)</span></span>
<span id="cb31-16"><a href="#cb31-16" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> google.oauth2 <span class="im">import</span> service_account</span>
<span id="cb31-17"><a href="#cb31-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-18"><a href="#cb31-18" aria-hidden="true" tabindex="-1"></a>credentials <span class="op">=</span> service_account.Credentials.from_service_account_file(</span>
<span id="cb31-19"><a href="#cb31-19" aria-hidden="true" tabindex="-1"></a> <span class="st">'service-account.json'</span></span>
<span id="cb31-20"><a href="#cb31-20" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb31-21"><a href="#cb31-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-22"><a href="#cb31-22" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(</span>
<span id="cb31-23"><a href="#cb31-23" aria-hidden="true" tabindex="-1"></a> <span class="st">"https://api.example.com/mcp"</span>,</span>
<span id="cb31-24"><a href="#cb31-24" aria-hidden="true" tabindex="-1"></a> auth<span class="op">=</span>credentials</span>
<span id="cb31-25"><a href="#cb31-25" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb31-26"><a href="#cb31-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-27"><a href="#cb31-27" aria-hidden="true" tabindex="-1"></a><span class="co"># Option 3: Manual token management</span></span>
<span id="cb31-28"><a href="#cb31-28" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(</span>
<span id="cb31-29"><a href="#cb31-29" aria-hidden="true" tabindex="-1"></a> <span class="st">"https://api.example.com/mcp"</span>,</span>
<span id="cb31-30"><a href="#cb31-30" aria-hidden="true" tabindex="-1"></a> headers<span class="op">=</span>{<span class="st">"Authorization"</span>: <span class="ss">f"Bearer </span><span class="sc">{</span>your_token<span class="sc">}</span><span class="ss">"</span>}</span>
<span id="cb31-31"><a href="#cb31-31" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb31-32"><a href="#cb31-32" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-33"><a href="#cb31-33" aria-hidden="true" tabindex="-1"></a><span class="co"># Option 4: Disable auto-browser (for servers/CI)</span></span>
<span id="cb31-34"><a href="#cb31-34" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(</span>
<span id="cb31-35"><a href="#cb31-35" aria-hidden="true" tabindex="-1"></a> <span class="st">"https://api.example.com/mcp"</span>,</span>
<span id="cb31-36"><a href="#cb31-36" aria-hidden="true" tabindex="-1"></a> auto_auth<span class="op">=</span><span class="va">False</span> <span class="co"># Raises error instead of opening browser</span></span>
<span id="cb31-37"><a href="#cb31-37" aria-hidden="true" tabindex="-1"></a>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
<p><strong>Environment variable support (for production):</strong></p>
<div class="sourceCode" id="cb32"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Set token via environment</span></span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">MCP_TOKEN</span><span class="op">=</span><span class="st">"your-token-here"</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div id="c8c19965" class="cell" data-execution_count="28">
<div class="sourceCode cell-code" id="cb33"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Automatically used if available</span></span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"https://api.example.com/mcp"</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="security-considerations" class="level3">
<h3 class="anchored" data-anchor-id="security-considerations">Security Considerations</h3>
<p><strong>Client-Side (mcp2py handles automatically):</strong> - ✅ Secure token storage - OAuth tokens cached in <code>~/.fastmcp/oauth-mcp-client-cache/</code> - ✅ PKCE support for OAuth flows (Proof Key for Code Exchange) - ✅ Automatic token refresh before expiration - ✅ Environment variable support (<code>MCP_TOKEN</code>)</p>
<p><strong>Server-Side (your responsibility when connecting):</strong> - Use HTTPS URLs for production servers (not HTTP) - Ensure the MCP servers you connect to implement proper authentication - Rotate tokens/credentials regularly - Never commit tokens to version control</p>
<p><strong>Best Practices:</strong></p>
<div id="8db99a9b" class="cell" data-execution_count="29">
<div class="sourceCode cell-code" id="cb34"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Good: Use environment variables</span></span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> os</span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"https://api.example.com/mcp"</span>, auth<span class="op">=</span>os.getenv(<span class="st">"MCP_TOKEN"</span>))</span>
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-5"><a href="#cb34-5" aria-hidden="true" tabindex="-1"></a><span class="co"># Good: HTTPS for production</span></span>
<span id="cb34-6"><a href="#cb34-6" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"https://api.example.com/mcp"</span>, auth<span class="op">=</span><span class="st">"oauth"</span>)</span>
<span id="cb34-7"><a href="#cb34-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-8"><a href="#cb34-8" aria-hidden="true" tabindex="-1"></a><span class="co"># Avoid: Hardcoded tokens in code</span></span>
<span id="cb34-9"><a href="#cb34-9" aria-hidden="true" tabindex="-1"></a><span class="co"># server = load("https://api.example.com/mcp", auth="sk-secret-123") # Don't do this!</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
</section>
<section id="advanced-features" class="level2">
<h2 class="anchored" data-anchor-id="advanced-features">Advanced Features</h2>
<section id="stub-generation" class="level3">
<h3 class="anchored" data-anchor-id="stub-generation">Stub Generation</h3>
<p>Stubs are automatically generated when you use <code>load()</code>. They’re cached to <code>~/.cache/mcp2py/stubs/</code> for reuse.</p>
<p><strong>Programmatic API:</strong></p>
<div id="7a42daad" class="cell" data-execution_count="30">
<div class="sourceCode cell-code" id="cb35"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Stubs auto-generated on load</span></span>
<span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"npx weather-server"</span>)</span>
<span id="cb35-5"><a href="#cb35-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-6"><a href="#cb35-6" aria-hidden="true" tabindex="-1"></a><span class="co"># Generate to specific path</span></span>
<span id="cb35-7"><a href="#cb35-7" aria-hidden="true" tabindex="-1"></a>stub_path <span class="op">=</span> server.generate_stubs(<span class="st">"./stubs/weather.pyi"</span>)</span>
<span id="cb35-8"><a href="#cb35-8" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f"Stub saved to: </span><span class="sc">{</span>stub_path<span class="sc">}</span><span class="ss">"</span>)</span>
<span id="cb35-9"><a href="#cb35-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-10"><a href="#cb35-10" aria-hidden="true" tabindex="-1"></a><span class="co"># Check cache location</span></span>
<span id="cb35-11"><a href="#cb35-11" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py.stubs <span class="im">import</span> get_stub_cache_path</span>
<span id="cb35-12"><a href="#cb35-12" aria-hidden="true" tabindex="-1"></a>cache_path <span class="op">=</span> get_stub_cache_path(<span class="st">"npx weather-server"</span>)</span>
<span id="cb35-13"><a href="#cb35-13" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f"Cached at: </span><span class="sc">{</span>cache_path<span class="sc">}</span><span class="ss">"</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="complete-client-example" class="level3">
<h3 class="anchored" data-anchor-id="complete-client-example">Complete Client Example</h3>
<div id="97801d42" class="cell" data-execution_count="31">
<div class="sourceCode cell-code" id="cb36"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a><span class="co">"""Full example of {python} as MCP client with all features."""</span></span>
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> anthropic</span>
<span id="cb36-4"><a href="#cb36-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-5"><a href="#cb36-5" aria-hidden="true" tabindex="-1"></a><span class="co"># Setup callbacks for server requests</span></span>
<span id="cb36-6"><a href="#cb36-6" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> handle_sampling(messages, model_prefs, system_prompt, max_tokens):</span>
<span id="cb36-7"><a href="#cb36-7" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Server wants LLM completion."""</span></span>
<span id="cb36-8"><a href="#cb36-8" aria-hidden="true" tabindex="-1"></a> client <span class="op">=</span> anthropic.Anthropic()</span>
<span id="cb36-9"><a href="#cb36-9" aria-hidden="true" tabindex="-1"></a> response <span class="op">=</span> client.messages.create(</span>
<span id="cb36-10"><a href="#cb36-10" aria-hidden="true" tabindex="-1"></a> model<span class="op">=</span><span class="st">"claude-3-5-sonnet-20241022"</span>,</span>
<span id="cb36-11"><a href="#cb36-11" aria-hidden="true" tabindex="-1"></a> messages<span class="op">=</span>messages,</span>
<span id="cb36-12"><a href="#cb36-12" aria-hidden="true" tabindex="-1"></a> system<span class="op">=</span>system_prompt,</span>
<span id="cb36-13"><a href="#cb36-13" aria-hidden="true" tabindex="-1"></a> max_tokens<span class="op">=</span>max_tokens</span>
<span id="cb36-14"><a href="#cb36-14" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb36-15"><a href="#cb36-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> response.content[<span class="dv">0</span>].text</span>
<span id="cb36-16"><a href="#cb36-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-17"><a href="#cb36-17" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> handle_elicitation(message, schema):</span>
<span id="cb36-18"><a href="#cb36-18" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Server needs user input."""</span></span>
<span id="cb36-19"><a href="#cb36-19" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="ss">🔔 Server asks: </span><span class="sc">{</span>message<span class="sc">}</span><span class="ss">"</span>)</span>
<span id="cb36-20"><a href="#cb36-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-21"><a href="#cb36-21" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> schema.get(<span class="st">"type"</span>) <span class="op">==</span> <span class="st">"string"</span>:</span>
<span id="cb36-22"><a href="#cb36-22" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">input</span>(<span class="st">"→ "</span>)</span>
<span id="cb36-23"><a href="#cb36-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-24"><a href="#cb36-24" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> schema.get(<span class="st">"type"</span>) <span class="op">==</span> <span class="st">"boolean"</span>:</span>
<span id="cb36-25"><a href="#cb36-25" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">input</span>(<span class="st">"→ (y/n): "</span>).lower() <span class="kw">in</span> [<span class="st">"y"</span>, <span class="st">"yes"</span>, <span class="st">"true"</span>]</span>
<span id="cb36-26"><a href="#cb36-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-27"><a href="#cb36-27" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> schema.get(<span class="st">"type"</span>) <span class="op">==</span> <span class="st">"object"</span>:</span>
<span id="cb36-28"><a href="#cb36-28" aria-hidden="true" tabindex="-1"></a> result <span class="op">=</span> {}</span>
<span id="cb36-29"><a href="#cb36-29" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> prop, details <span class="kw">in</span> schema.get(<span class="st">"properties"</span>, {}).items():</span>
<span id="cb36-30"><a href="#cb36-30" aria-hidden="true" tabindex="-1"></a> result[prop] <span class="op">=</span> <span class="bu">input</span>(<span class="ss">f" </span><span class="sc">{</span>prop<span class="sc">}</span><span class="ss"> (</span><span class="sc">{</span>details<span class="sc">.</span>get(<span class="st">'description'</span>, <span class="st">''</span>)<span class="sc">}</span><span class="ss">): "</span>)</span>
<span id="cb36-31"><a href="#cb36-31" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> result</span>
<span id="cb36-32"><a href="#cb36-32" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-33"><a href="#cb36-33" aria-hidden="true" tabindex="-1"></a> <span class="im">import</span> json</span>
<span id="cb36-34"><a href="#cb36-34" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> json.loads(<span class="bu">input</span>(<span class="st">"→ (JSON): "</span>))</span>
<span id="cb36-35"><a href="#cb36-35" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-36"><a href="#cb36-36" aria-hidden="true" tabindex="-1"></a><span class="co"># Connect to server with all features</span></span>
<span id="cb36-37"><a href="#cb36-37" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(</span>
<span id="cb36-38"><a href="#cb36-38" aria-hidden="true" tabindex="-1"></a> <span class="st">"npx travel-booking-server"</span>,</span>
<span id="cb36-39"><a href="#cb36-39" aria-hidden="true" tabindex="-1"></a> on_sampling<span class="op">=</span>handle_sampling,</span>
<span id="cb36-40"><a href="#cb36-40" aria-hidden="true" tabindex="-1"></a> on_elicitation<span class="op">=</span>handle_elicitation,</span>
<span id="cb36-41"><a href="#cb36-41" aria-hidden="true" tabindex="-1"></a> roots<span class="op">=</span><span class="st">"/home/user/travel-docs"</span></span>
<span id="cb36-42"><a href="#cb36-42" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb36-43"><a href="#cb36-43" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-44"><a href="#cb36-44" aria-hidden="true" tabindex="-1"></a><span class="co"># Use the server - callbacks invoked automatically when needed</span></span>
<span id="cb36-45"><a href="#cb36-45" aria-hidden="true" tabindex="-1"></a>booking <span class="op">=</span> server.book_flight(destination<span class="op">=</span><span class="st">"Barcelona"</span>, dates<span class="op">=</span><span class="st">"June 15-22"</span>)</span>
<span id="cb36-46"><a href="#cb36-46" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(booking)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="inspection" class="level3">
<h3 class="anchored" data-anchor-id="inspection">Inspection</h3>
<div id="5206b775" class="cell" data-execution_count="32">
<div class="sourceCode cell-code" id="cb37"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb37-3"><a href="#cb37-3" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(<span class="st">"npx my-server"</span>)</span>
<span id="cb37-4"><a href="#cb37-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb37-5"><a href="#cb37-5" aria-hidden="true" tabindex="-1"></a><span class="co"># List all available tools</span></span>
<span id="cb37-6"><a href="#cb37-6" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(server.tools) <span class="co"># List of tool schemas for AI SDKs</span></span>
<span id="cb37-7"><a href="#cb37-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb37-8"><a href="#cb37-8" aria-hidden="true" tabindex="-1"></a><span class="co"># Get tool info</span></span>
<span id="cb37-9"><a href="#cb37-9" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(server.get_weather.__doc__)</span>
<span id="cb37-10"><a href="#cb37-10" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(server.get_weather.__signature__)</span>
<span id="cb37-11"><a href="#cb37-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb37-12"><a href="#cb37-12" aria-hidden="true" tabindex="-1"></a><span class="co"># List resources</span></span>
<span id="cb37-13"><a href="#cb37-13" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(server.resources)</span>
<span id="cb37-14"><a href="#cb37-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb37-15"><a href="#cb37-15" aria-hidden="true" tabindex="-1"></a><span class="co"># List prompts</span></span>
<span id="cb37-16"><a href="#cb37-16" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(server.prompts)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="middleware-hooks" class="level3">
<h3 class="anchored" data-anchor-id="middleware-hooks">Middleware & Hooks</h3>
<div id="05d8cdba" class="cell" data-execution_count="33">
<div class="sourceCode cell-code" id="cb38"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb38-3"><a href="#cb38-3" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> log_tool_calls(tool_name: <span class="bu">str</span>, args: <span class="bu">dict</span>, result: <span class="bu">dict</span>):</span>
<span id="cb38-4"><a href="#cb38-4" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Called </span><span class="sc">{</span>tool_name<span class="sc">}</span><span class="ss"> with </span><span class="sc">{</span>args<span class="sc">}</span><span class="ss"> → </span><span class="sc">{</span>result<span class="sc">}</span><span class="ss">"</span>)</span>
<span id="cb38-5"><a href="#cb38-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb38-6"><a href="#cb38-6" aria-hidden="true" tabindex="-1"></a>server <span class="op">=</span> load(</span>
<span id="cb38-7"><a href="#cb38-7" aria-hidden="true" tabindex="-1"></a> <span class="st">"npx my-server"</span>,</span>
<span id="cb38-8"><a href="#cb38-8" aria-hidden="true" tabindex="-1"></a> on_tool_call<span class="op">=</span>log_tool_calls,</span>
<span id="cb38-9"><a href="#cb38-9" aria-hidden="true" tabindex="-1"></a> timeout<span class="op">=</span><span class="fl">30.0</span></span>
<span id="cb38-10"><a href="#cb38-10" aria-hidden="true" tabindex="-1"></a>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
</section>
<section id="implementation-priorities" class="level2">
<h2 class="anchored" data-anchor-id="implementation-priorities">Implementation Priorities</h2>
<section id="phase-1-core-functionality" class="level3">
<h3 class="anchored" data-anchor-id="phase-1-core-functionality">Phase 1: Core Functionality</h3>
<ol type="1">
<li><code>load()</code> function with stdio transport</li>
<li>Tool → function mapping with type hints</li>
<li>Simple resource access</li>
<li>Prompt → template function mapping</li>
<li><code>.tools</code> attribute for AI SDK integration</li>
</ol>
</section>
<section id="phase-2-developer-experience" class="level3">
<h3 class="anchored" data-anchor-id="phase-2-developer-experience">Phase 2: Developer Experience</h3>
<ol type="1">
<li>Stub generation for IDE support</li>
<li>Server registry (<code>~/.config/mcp2py/servers.json</code>)</li>
<li>Context manager protocol</li>
<li>Better error messages and exceptions</li>
</ol>
</section>
<section id="phase-3-advanced-features" class="level3">
<h3 class="anchored" data-anchor-id="phase-3-advanced-features">Phase 3: Advanced Features</h3>
<ol type="1">
<li><code>aload()</code> for async support</li>
<li>SSE transport for HTTP servers</li>
<li>Middleware/hooks system</li>
<li>Sampling and elicitation callbacks</li>
</ol>
</section>
</section>
<section id="design-principles" class="level2">
<h2 class="anchored" data-anchor-id="design-principles">Design Principles</h2>
<ol type="1">
<li><strong>Delightful Defaults</strong>: Authentication, sampling, elicitation all work automatically</li>
<li><strong>No Ceiling</strong>: Every default can be overridden for production use cases</li>
<li><strong>Beginner-Friendly</strong>: Data analysts and researchers can start immediately</li>
<li><strong>Production-Ready</strong>: Full control for developers building apps</li>
<li><strong>Progressive Disclosure</strong>: Simple by default, powerful when you need it</li>
<li><strong>Type Safety</strong>: Generate types wherever possible for IDE support</li>
<li><strong>{python}ic</strong>: Convert MCP conventions to {python} conventions automatically</li>
<li><strong>Clear Errors</strong>: Helpful messages when things go wrong, with suggestions</li>
</ol>
</section>
<section id="complete-examples" class="level2">
<h2 class="anchored" data-anchor-id="complete-examples">Complete Examples</h2>
<section id="example-1-synchronous---weather-analysis-with-dspy" class="level3">
<h3 class="anchored" data-anchor-id="example-1-synchronous---weather-analysis-with-dspy">Example 1: Synchronous - Weather Analysis with DSPy</h3>
<div id="6a0d635b" class="cell" data-execution_count="34">
<div class="sourceCode cell-code" id="cb39"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env {python}3</span></span>
<span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a><span class="co">"""Analyze weather alerts using DSPy and MCP."""</span></span>
<span id="cb39-3"><a href="#cb39-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-4"><a href="#cb39-4" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> load</span>
<span id="cb39-5"><a href="#cb39-5" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> dspy</span>
<span id="cb39-6"><a href="#cb39-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-7"><a href="#cb39-7" aria-hidden="true" tabindex="-1"></a><span class="co"># Configure DSPy</span></span>
<span id="cb39-8"><a href="#cb39-8" aria-hidden="true" tabindex="-1"></a>dspy.configure(lm<span class="op">=</span>dspy.LM(<span class="st">"openai/gpt-4o-mini"</span>))</span>
<span id="cb39-9"><a href="#cb39-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-10"><a href="#cb39-10" aria-hidden="true" tabindex="-1"></a><span class="co"># Load MCP weather server</span></span>
<span id="cb39-11"><a href="#cb39-11" aria-hidden="true" tabindex="-1"></a>weather <span class="op">=</span> load(<span class="st">"npx -y @h1deya/mcp-server-weather"</span>)</span>
<span id="cb39-12"><a href="#cb39-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-13"><a href="#cb39-13" aria-hidden="true" tabindex="-1"></a><span class="co"># Define DSPy signature</span></span>
<span id="cb39-14"><a href="#cb39-14" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> WeatherAnalyzer(dspy.Signature):</span>
<span id="cb39-15"><a href="#cb39-15" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Analyze weather alerts and provide recommendations."""</span></span>
<span id="cb39-16"><a href="#cb39-16" aria-hidden="true" tabindex="-1"></a> state: <span class="bu">str</span> <span class="op">=</span> dspy.InputField()</span>
<span id="cb39-17"><a href="#cb39-17" aria-hidden="true" tabindex="-1"></a> analysis: <span class="bu">str</span> <span class="op">=</span> dspy.OutputField(desc<span class="op">=</span><span class="st">"Weather analysis and travel recommendations"</span>)</span>
<span id="cb39-18"><a href="#cb39-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-19"><a href="#cb39-19" aria-hidden="true" tabindex="-1"></a><span class="co"># Create agent with MCP tools</span></span>
<span id="cb39-20"><a href="#cb39-20" aria-hidden="true" tabindex="-1"></a>agent <span class="op">=</span> dspy.ReAct(WeatherAnalyzer, tools<span class="op">=</span>weather.tools)</span>
<span id="cb39-21"><a href="#cb39-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-22"><a href="#cb39-22" aria-hidden="true" tabindex="-1"></a><span class="co"># Analyze weather for multiple states</span></span>
<span id="cb39-23"><a href="#cb39-23" aria-hidden="true" tabindex="-1"></a>states <span class="op">=</span> [<span class="st">"CA"</span>, <span class="st">"NY"</span>, <span class="st">"TX"</span>, <span class="st">"FL"</span>]</span>
<span id="cb39-24"><a href="#cb39-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb39-25"><a href="#cb39-25" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> state <span class="kw">in</span> states:</span>
<span id="cb39-26"><a href="#cb39-26" aria-hidden="true" tabindex="-1"></a> <span class="co"># Agent automatically calls weather.get_alerts() and weather.get_forecast()</span></span>
<span id="cb39-27"><a href="#cb39-27" aria-hidden="true" tabindex="-1"></a> result <span class="op">=</span> agent(state<span class="op">=</span>state)</span>
<span id="cb39-28"><a href="#cb39-28" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="sc">{</span>state<span class="sc">}</span><span class="ss">:"</span>)</span>
<span id="cb39-29"><a href="#cb39-29" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(result.analysis)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="example-2-asynchronous---travel-booking-system" class="level3">
<h3 class="anchored" data-anchor-id="example-2-asynchronous---travel-booking-system">Example 2: Asynchronous - Travel Booking System</h3>
<div id="3a8313e0" class="cell" data-execution_count="35">
<div class="sourceCode cell-code" id="cb40"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb40-1"><a href="#cb40-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env {python}3</span></span>
<span id="cb40-2"><a href="#cb40-2" aria-hidden="true" tabindex="-1"></a><span class="co">"""Async travel booking system with MCP and Anthropic."""</span></span>
<span id="cb40-3"><a href="#cb40-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-4"><a href="#cb40-4" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> asyncio</span>
<span id="cb40-5"><a href="#cb40-5" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mcp2py <span class="im">import</span> aload</span>
<span id="cb40-6"><a href="#cb40-6" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> anthropic</span>
<span id="cb40-7"><a href="#cb40-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-8"><a href="#cb40-8" aria-hidden="true" tabindex="-1"></a><span class="cf">async</span> <span class="kw">def</span> book_trip(user_request: <span class="bu">str</span>):</span>
<span id="cb40-9"><a href="#cb40-9" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Book a trip using MCP travel server and Claude."""</span></span>
<span id="cb40-10"><a href="#cb40-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-11"><a href="#cb40-11" aria-hidden="true" tabindex="-1"></a> <span class="co"># Load async MCP server</span></span>
<span id="cb40-12"><a href="#cb40-12" aria-hidden="true" tabindex="-1"></a> travel <span class="op">=</span> <span class="cf">await</span> aload(<span class="st">"</span><span class="sc">{python}</span><span class="st"> travel_server.py"</span>)</span>
<span id="cb40-13"><a href="#cb40-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-14"><a href="#cb40-14" aria-hidden="true" tabindex="-1"></a> <span class="co"># Setup Anthropic client</span></span>
<span id="cb40-15"><a href="#cb40-15" aria-hidden="true" tabindex="-1"></a> client <span class="op">=</span> anthropic.Anthropic()</span>
<span id="cb40-16"><a href="#cb40-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-17"><a href="#cb40-17" aria-hidden="true" tabindex="-1"></a> <span class="co"># Initial request to Claude with MCP tools</span></span>
<span id="cb40-18"><a href="#cb40-18" aria-hidden="true" tabindex="-1"></a> response <span class="op">=</span> client.messages.create(</span>
<span id="cb40-19"><a href="#cb40-19" aria-hidden="true" tabindex="-1"></a> model<span class="op">=</span><span class="st">"claude-3-5-sonnet-20241022"</span>,</span>
<span id="cb40-20"><a href="#cb40-20" aria-hidden="true" tabindex="-1"></a> max_tokens<span class="op">=</span><span class="dv">2048</span>,</span>
<span id="cb40-21"><a href="#cb40-21" aria-hidden="true" tabindex="-1"></a> tools<span class="op">=</span>travel.tools, <span class="co"># MCP tools passed to Claude</span></span>
<span id="cb40-22"><a href="#cb40-22" aria-hidden="true" tabindex="-1"></a> messages<span class="op">=</span>[{<span class="st">"role"</span>: <span class="st">"user"</span>, <span class="st">"content"</span>: user_request}]</span>
<span id="cb40-23"><a href="#cb40-23" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb40-24"><a href="#cb40-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-25"><a href="#cb40-25" aria-hidden="true" tabindex="-1"></a> <span class="co"># Handle tool calls in a loop</span></span>
<span id="cb40-26"><a href="#cb40-26" aria-hidden="true" tabindex="-1"></a> messages <span class="op">=</span> [{<span class="st">"role"</span>: <span class="st">"user"</span>, <span class="st">"content"</span>: user_request}]</span>
<span id="cb40-27"><a href="#cb40-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-28"><a href="#cb40-28" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span> response.stop_reason <span class="op">==</span> <span class="st">"tool_use"</span>:</span>
<span id="cb40-29"><a href="#cb40-29" aria-hidden="true" tabindex="-1"></a> <span class="co"># Extract tool calls from response</span></span>
<span id="cb40-30"><a href="#cb40-30" aria-hidden="true" tabindex="-1"></a> tool_results <span class="op">=</span> []</span>
<span id="cb40-31"><a href="#cb40-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-32"><a href="#cb40-32" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> content_block <span class="kw">in</span> response.content:</span>
<span id="cb40-33"><a href="#cb40-33" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> content_block.<span class="bu">type</span> <span class="op">==</span> <span class="st">"tool_use"</span>:</span>
<span id="cb40-34"><a href="#cb40-34" aria-hidden="true" tabindex="-1"></a> <span class="co"># Call MCP tool asynchronously</span></span>
<span id="cb40-35"><a href="#cb40-35" aria-hidden="true" tabindex="-1"></a> tool_name <span class="op">=</span> content_block.name</span>
<span id="cb40-36"><a href="#cb40-36" aria-hidden="true" tabindex="-1"></a> tool_args <span class="op">=</span> content_block.<span class="bu">input</span></span>
<span id="cb40-37"><a href="#cb40-37" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-38"><a href="#cb40-38" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Calling </span><span class="sc">{</span>tool_name<span class="sc">}</span><span class="ss">(</span><span class="sc">{</span>tool_args<span class="sc">}</span><span class="ss">)..."</span>)</span>
<span id="cb40-39"><a href="#cb40-39" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-40"><a href="#cb40-40" aria-hidden="true" tabindex="-1"></a> <span class="co"># Execute tool via MCP</span></span>
<span id="cb40-41"><a href="#cb40-41" aria-hidden="true" tabindex="-1"></a> tool_func <span class="op">=</span> <span class="bu">getattr</span>(travel, tool_name)</span>
<span id="cb40-42"><a href="#cb40-42" aria-hidden="true" tabindex="-1"></a> result <span class="op">=</span> <span class="cf">await</span> tool_func(<span class="op">**</span>tool_args)</span>
<span id="cb40-43"><a href="#cb40-43" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-44"><a href="#cb40-44" aria-hidden="true" tabindex="-1"></a> tool_results.append({</span>
<span id="cb40-45"><a href="#cb40-45" aria-hidden="true" tabindex="-1"></a> <span class="st">"type"</span>: <span class="st">"tool_result"</span>,</span>
<span id="cb40-46"><a href="#cb40-46" aria-hidden="true" tabindex="-1"></a> <span class="st">"tool_use_id"</span>: content_block.<span class="bu">id</span>,</span>
<span id="cb40-47"><a href="#cb40-47" aria-hidden="true" tabindex="-1"></a> <span class="st">"content"</span>: <span class="bu">str</span>(result)</span>
<span id="cb40-48"><a href="#cb40-48" aria-hidden="true" tabindex="-1"></a> })</span>
<span id="cb40-49"><a href="#cb40-49" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-50"><a href="#cb40-50" aria-hidden="true" tabindex="-1"></a> <span class="co"># Add assistant response and tool results to conversation</span></span>
<span id="cb40-51"><a href="#cb40-51" aria-hidden="true" tabindex="-1"></a> messages.append({<span class="st">"role"</span>: <span class="st">"assistant"</span>, <span class="st">"content"</span>: response.content})</span>
<span id="cb40-52"><a href="#cb40-52" aria-hidden="true" tabindex="-1"></a> messages.append({<span class="st">"role"</span>: <span class="st">"user"</span>, <span class="st">"content"</span>: tool_results})</span>
<span id="cb40-53"><a href="#cb40-53" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-54"><a href="#cb40-54" aria-hidden="true" tabindex="-1"></a> <span class="co"># Continue conversation</span></span>
<span id="cb40-55"><a href="#cb40-55" aria-hidden="true" tabindex="-1"></a> response <span class="op">=</span> client.messages.create(</span>
<span id="cb40-56"><a href="#cb40-56" aria-hidden="true" tabindex="-1"></a> model<span class="op">=</span><span class="st">"claude-3-5-sonnet-20241022"</span>,</span>
<span id="cb40-57"><a href="#cb40-57" aria-hidden="true" tabindex="-1"></a> max_tokens<span class="op">=</span><span class="dv">2048</span>,</span>
<span id="cb40-58"><a href="#cb40-58" aria-hidden="true" tabindex="-1"></a> tools<span class="op">=</span>travel.tools,</span>
<span id="cb40-59"><a href="#cb40-59" aria-hidden="true" tabindex="-1"></a> messages<span class="op">=</span>messages</span>
<span id="cb40-60"><a href="#cb40-60" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb40-61"><a href="#cb40-61" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-62"><a href="#cb40-62" aria-hidden="true" tabindex="-1"></a> <span class="co"># Extract final response</span></span>
<span id="cb40-63"><a href="#cb40-63" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> response.content[<span class="dv">0</span>].text</span>
<span id="cb40-64"><a href="#cb40-64" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-65"><a href="#cb40-65" aria-hidden="true" tabindex="-1"></a><span class="cf">async</span> <span class="kw">def</span> main():</span>
<span id="cb40-66"><a href="#cb40-66" aria-hidden="true" tabindex="-1"></a> result <span class="op">=</span> <span class="cf">await</span> book_trip(</span>
<span id="cb40-67"><a href="#cb40-67" aria-hidden="true" tabindex="-1"></a> <span class="st">"Book a round-trip flight from SFO to JFK on Sept 1-8, 2025. "</span></span>
<span id="cb40-68"><a href="#cb40-68" aria-hidden="true" tabindex="-1"></a> <span class="st">"My name is Adam Smith. I prefer window seats and morning flights."</span></span>
<span id="cb40-69"><a href="#cb40-69" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb40-70"><a href="#cb40-70" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"="</span><span class="op">*</span><span class="dv">60</span>)</span>
<span id="cb40-71"><a href="#cb40-71" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"BOOKING RESULT:"</span>)</span>
<span id="cb40-72"><a href="#cb40-72" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span><span class="op">*</span><span class="dv">60</span>)</span>
<span id="cb40-73"><a href="#cb40-73" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(result)</span>
<span id="cb40-74"><a href="#cb40-74" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb40-75"><a href="#cb40-75" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">"__main__"</span>:</span>
<span id="cb40-76"><a href="#cb40-76" aria-hidden="true" tabindex="-1"></a> asyncio.run(main())</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</div>
</section>
<section id="example-3-simple-synchronous---direct-tool-calls" class="level3">