@sbissoli/mcp-surface 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,21 @@ seguinte — cada servidor faz bump explícito).
8
8
  e, pela regra da trava, obrigaria cada servidor a subir de versão sem ter mudado nada.
9
9
  Mudança de normalização é major (ou minor em 0.x), com nota de migração.
10
10
 
11
+ ## [0.2.0] — 2026-10-04
12
+
13
+ A normalização NÃO muda: todo `surface.lock.json` continua conferindo. Minor porque a
14
+ superfície publicada do pacote cresce (subpath novo).
15
+
16
+ ### Adicionado
17
+
18
+ - **`@sbissoli/mcp-surface/cliente`**: o teste com forma de cliente, que nasceu no
19
+ ilo-mcp-server 1.3.0 (ideia de leitor, https://dev.to/arhancanli/comment/3g4i4).
20
+ `conectarComoCliente` (transporte em memória com JSON no fio), `chamarComoCliente`
21
+ (`tools/list` antes do `tools/call`, lança se o `Client` reprovar ou vier `isError`),
22
+ `controlesNegativos` (quebras derivadas do `outputSchema` listado, mais a armadilha do
23
+ `tools/list` ausente) e `quebrasDoSchema`. `@modelcontextprotocol/client` entra como peer
24
+ OPCIONAL; a raiz do pacote continua sem o `Client`, que o Worker não pode carregar.
25
+
11
26
  ## [0.1.1] — 2026-10-02
12
27
 
13
28
  Achados da adoção nos irmãos, no mesmo dia. A normalização NÃO muda: todo
package/README.md CHANGED
@@ -70,6 +70,35 @@ travar superfície nova sob a versão antiga.
70
70
  Fluxo de quem muda a superfície: `npm version <nível> --no-git-tag-version` →
71
71
  `npm run surface:lock` → commitar o lock junto.
72
72
 
73
+ ## Teste com forma de cliente (`@sbissoli/mcp-surface/cliente`)
74
+
75
+ O servidor interrogado pelo `Client` do SDK, que reprova o resultado de `tools/call`
76
+ contra o `outputSchema` **listado**. Assim o teste falha como a sessão do usuário falharia,
77
+ sem um validador escolhido por nós. É subpath à parte porque o `Client` compila schemas
78
+ com Ajv (`new Function`), que o Worker proíbe: só se importa em teste Node, e o
79
+ `@modelcontextprotocol/client` é peer opcional.
80
+
81
+ ```ts
82
+ import { chamarComoCliente, conectarComoCliente, controlesNegativos } from "@sbissoli/mcp-surface/cliente";
83
+
84
+ const client = await conectarComoCliente(buildServer(env));
85
+ const r = await chamarComoCliente(client, "minha_tool", { x: 1 }); // lança se o Client reprovar ou se vier isError
86
+
87
+ const vs = await controlesNegativos(() => buildServer(env), "minha_tool", { x: 1 });
88
+ for (const v of vs) expect(v.obtido, `${v.descricao}: ${v.mensagem ?? ""}`).toBe(v.esperado);
89
+ ```
90
+
91
+ - `conectarComoCliente` passa toda mensagem do servidor por JSON antes de entregá-la: o
92
+ transporte em memória não serializa, e chave `undefined` só some no fio.
93
+ - `chamarComoCliente` faz `tools/list` antes do primeiro `tools/call` da conexão. Sem
94
+ isso, o `Client` (2.0 a 2.2) devolve o resultado sem validar.
95
+ - `controlesNegativos` adultera o resultado entre servidor e cliente, com quebras
96
+ **derivadas do schema listado**: `structuredContent` ausente, cada obrigatório ausente e
97
+ o primeiro obrigatório de tipo errado. Cada quebra tem de fazer a chamada falhar. O
98
+ último veredito é a armadilha (sem `tools/list`, a quebra passa calada); se o SDK mudar,
99
+ ele acusa. Quebras do próprio servidor, como campo a mais onde o schema fecha o objeto,
100
+ entram pelo 4º argumento.
101
+
73
102
  ## Observações
74
103
 
75
104
  - Uma atualização do SDK que mexa nas `capabilities` também acende a trava — de propósito:
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Teste com forma de cliente: o servidor interrogado pelo `Client` do SDK, que
3
+ * reprova o resultado de `tools/call` contra o `outputSchema` LISTADO — o teste
4
+ * falha como a sessão do usuário falharia, sem validador escolhido por nós.
5
+ *
6
+ * Ideia de leitor (https://dev.to/arhancanli/comment/3g4i4); o circuito nasceu
7
+ * no ilo-mcp-server 1.3.0 e mora aqui para os sete servidores o usarem igual.
8
+ *
9
+ * Subpath próprio (`@sbissoli/mcp-surface/cliente`), fora da raiz do pacote: o
10
+ * `Client` compila os schemas com Ajv (`new Function`), que o runtime da
11
+ * Cloudflare proíbe. Importar daqui é coisa de teste em Node, nunca de Worker.
12
+ *
13
+ * Duas armadilhas que este módulo fecha, medidas no ilo:
14
+ * - o `Client` só valida contra o schema que tem em cache do `tools/list` —
15
+ * sem `listTools` antes, `callTool` devolve o que recebeu sem validar;
16
+ * - o `InMemoryTransport` não serializa: chave `undefined` sobrevive em memória
17
+ * e some no fio (`JSON.stringify`). Aqui toda mensagem do servidor passa por
18
+ * JSON antes de chegar ao cliente, como passaria pela rede.
19
+ */
20
+ import { Client } from "@modelcontextprotocol/client";
21
+ import type { ServidorConectavel } from "./memoria.js";
22
+ /** Resultado de `tools/call` como viaja no fio (o que uma quebra recebe). */
23
+ export type ResultadoNoFio = {
24
+ content?: unknown;
25
+ structuredContent?: Record<string, unknown>;
26
+ } & Record<string, unknown>;
27
+ export interface OpcoesConexao {
28
+ /** Mexe no resultado de `tools/call` entre servidor e cliente (controle negativo). */
29
+ adulterar?: (r: ResultadoNoFio) => void;
30
+ }
31
+ export declare function conectarComoCliente(server: ServidorConectavel, opcoes?: OpcoesConexao): Promise<Client>;
32
+ /**
33
+ * Chamada que TEM de dar certo, no percurso do cliente. Lança quando o `Client`
34
+ * reprova o resultado e também quando a tool responde `isError` — o servidor do
35
+ * SDK v2 valida a própria saída e, se ela não obedece, devolve o erro em
36
+ * `isError` ("Output validation error"), não em exceção. Para afirmar uma
37
+ * recusa de propósito (parâmetro inválido), use `client.callTool` direto.
38
+ */
39
+ export declare function chamarComoCliente(client: Client, nome: string, args?: Record<string, unknown>): Promise<{
40
+ [x: string]: unknown;
41
+ _meta?: {
42
+ [x: string]: unknown;
43
+ "io.modelcontextprotocol/serverInfo"?: {
44
+ version: string;
45
+ websiteUrl?: string | undefined;
46
+ description?: string | undefined;
47
+ icons?: {
48
+ src: string;
49
+ mimeType?: string | undefined;
50
+ sizes?: string[] | undefined;
51
+ theme?: "dark" | "light" | undefined;
52
+ }[] | undefined;
53
+ name: string;
54
+ title?: string | undefined;
55
+ } | undefined;
56
+ } | undefined;
57
+ content: ({
58
+ type: "text";
59
+ text: string;
60
+ annotations?: {
61
+ audience?: ("assistant" | "user")[] | undefined;
62
+ priority?: number | undefined;
63
+ lastModified?: string | undefined;
64
+ } | undefined;
65
+ _meta?: {
66
+ [x: string]: unknown;
67
+ } | undefined;
68
+ } | {
69
+ type: "image";
70
+ data: string;
71
+ mimeType: string;
72
+ annotations?: {
73
+ audience?: ("assistant" | "user")[] | undefined;
74
+ priority?: number | undefined;
75
+ lastModified?: string | undefined;
76
+ } | undefined;
77
+ _meta?: {
78
+ [x: string]: unknown;
79
+ } | undefined;
80
+ } | {
81
+ type: "audio";
82
+ data: string;
83
+ mimeType: string;
84
+ annotations?: {
85
+ audience?: ("assistant" | "user")[] | undefined;
86
+ priority?: number | undefined;
87
+ lastModified?: string | undefined;
88
+ } | undefined;
89
+ _meta?: {
90
+ [x: string]: unknown;
91
+ } | undefined;
92
+ } | {
93
+ uri: string;
94
+ description?: string | undefined;
95
+ mimeType?: string | undefined;
96
+ size?: number | undefined;
97
+ annotations?: {
98
+ audience?: ("assistant" | "user")[] | undefined;
99
+ priority?: number | undefined;
100
+ lastModified?: string | undefined;
101
+ } | undefined;
102
+ _meta?: {
103
+ [x: string]: unknown;
104
+ } | undefined;
105
+ icons?: {
106
+ src: string;
107
+ mimeType?: string | undefined;
108
+ sizes?: string[] | undefined;
109
+ theme?: "dark" | "light" | undefined;
110
+ }[] | undefined;
111
+ name: string;
112
+ title?: string | undefined;
113
+ type: "resource_link";
114
+ } | {
115
+ type: "resource";
116
+ resource: {
117
+ uri: string;
118
+ mimeType?: string | undefined;
119
+ _meta?: {
120
+ [x: string]: unknown;
121
+ } | undefined;
122
+ text: string;
123
+ } | {
124
+ uri: string;
125
+ mimeType?: string | undefined;
126
+ _meta?: {
127
+ [x: string]: unknown;
128
+ } | undefined;
129
+ blob: string;
130
+ };
131
+ annotations?: {
132
+ audience?: ("assistant" | "user")[] | undefined;
133
+ priority?: number | undefined;
134
+ lastModified?: string | undefined;
135
+ } | undefined;
136
+ _meta?: {
137
+ [x: string]: unknown;
138
+ } | undefined;
139
+ })[];
140
+ structuredContent?: unknown;
141
+ isError?: boolean | undefined;
142
+ }>;
143
+ export interface Quebra {
144
+ descricao: string;
145
+ adulterar: (r: ResultadoNoFio) => void;
146
+ }
147
+ /**
148
+ * As quebras genéricas, DERIVADAS do schema listado (não de nomes escritos à
149
+ * mão): `structuredContent` ausente, cada campo obrigatório ausente, o primeiro
150
+ * obrigatório com tipo declarado trocado de tipo.
151
+ */
152
+ export declare function quebrasDoSchema(outputSchema: Record<string, unknown>): Quebra[];
153
+ export interface Veredito {
154
+ descricao: string;
155
+ /** O que o circuito exige: o cliente reprovar a quebra (ou, na armadilha, deixá-la passar). */
156
+ esperado: "reprova" | "passa";
157
+ /** `isError`: a chamada-base falhou como tool — o caso está mal montado. */
158
+ obtido: "reprova" | "passa" | "isError";
159
+ mensagem?: string;
160
+ }
161
+ /**
162
+ * Controle negativo pelo lado do resultado: o servidor responde certo, o que
163
+ * chega ao cliente está quebrado, e a chamada TEM de falhar. Um servidor novo
164
+ * por quebra (`fabrica`), porque cada conexão leva a sua adulteração.
165
+ *
166
+ * Inclui a armadilha como veredito esperado "passa": sem `tools/list` antes, a
167
+ * primeira quebra passa calada. Se um dia o SDK passar a validar sem a lista,
168
+ * esse veredito acusa — e a regra do `listTools` pode ser revista.
169
+ *
170
+ * O teste afirma `obtido === esperado` em cada veredito; a `descricao` diz qual.
171
+ */
172
+ export declare function controlesNegativos(fabrica: () => ServidorConectavel | Promise<ServidorConectavel>, nome: string, args?: Record<string, unknown>, extras?: Quebra[]): Promise<Veredito[]>;
173
+ //# sourceMappingURL=cliente.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cliente.d.ts","sourceRoot":"","sources":["../src/cliente.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAGtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,6EAA6E;AAC7E,MAAM,MAAM,cAAc,GAAG;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1H,MAAM,WAAW,aAAa;IAC5B,sFAAsF;IACtF,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;CACzC;AAID,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,kBAAkB,EAAE,MAAM,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAYjH;AAWD;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAOvG;AAED,MAAM,WAAW,MAAM;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;CACxC;AASD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAoB/E;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,+FAA+F;IAC/F,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC;IAC9B,4EAA4E;IAC5E,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,EAC/D,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EAClC,MAAM,GAAE,MAAM,EAAO,GACpB,OAAO,CAAC,QAAQ,EAAE,CAAC,CAoCrB"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Teste com forma de cliente: o servidor interrogado pelo `Client` do SDK, que
3
+ * reprova o resultado de `tools/call` contra o `outputSchema` LISTADO — o teste
4
+ * falha como a sessão do usuário falharia, sem validador escolhido por nós.
5
+ *
6
+ * Ideia de leitor (https://dev.to/arhancanli/comment/3g4i4); o circuito nasceu
7
+ * no ilo-mcp-server 1.3.0 e mora aqui para os sete servidores o usarem igual.
8
+ *
9
+ * Subpath próprio (`@sbissoli/mcp-surface/cliente`), fora da raiz do pacote: o
10
+ * `Client` compila os schemas com Ajv (`new Function`), que o runtime da
11
+ * Cloudflare proíbe. Importar daqui é coisa de teste em Node, nunca de Worker.
12
+ *
13
+ * Duas armadilhas que este módulo fecha, medidas no ilo:
14
+ * - o `Client` só valida contra o schema que tem em cache do `tools/list` —
15
+ * sem `listTools` antes, `callTool` devolve o que recebeu sem validar;
16
+ * - o `InMemoryTransport` não serializa: chave `undefined` sobrevive em memória
17
+ * e some no fio (`JSON.stringify`). Aqui toda mensagem do servidor passa por
18
+ * JSON antes de chegar ao cliente, como passaria pela rede.
19
+ */
20
+ import { Client } from "@modelcontextprotocol/client";
21
+ import { InMemoryTransport } from "@modelcontextprotocol/server";
22
+ const listados = new WeakSet();
23
+ export async function conectarComoCliente(server, opcoes = {}) {
24
+ const [lado, ladoServidor] = InMemoryTransport.createLinkedPair();
25
+ const enviar = ladoServidor.send.bind(ladoServidor);
26
+ ladoServidor.send = async (mensagem, extra) => {
27
+ const noFio = JSON.parse(JSON.stringify(mensagem));
28
+ const r = noFio.result;
29
+ if (opcoes.adulterar && r && "content" in r)
30
+ opcoes.adulterar(r);
31
+ return enviar(noFio, extra);
32
+ };
33
+ const client = new Client({ name: "mcp-surface/cliente", version: "0.0.0" });
34
+ await Promise.all([server.connect(ladoServidor), client.connect(lado)]);
35
+ return client;
36
+ }
37
+ /** O percurso do cliente: `tools/list` (uma vez por conexão) → `tools/call`. */
38
+ async function percorrer(client, nome, args) {
39
+ if (!listados.has(client)) {
40
+ await client.listTools();
41
+ listados.add(client);
42
+ }
43
+ return client.callTool({ name: nome, arguments: args });
44
+ }
45
+ /**
46
+ * Chamada que TEM de dar certo, no percurso do cliente. Lança quando o `Client`
47
+ * reprova o resultado e também quando a tool responde `isError` — o servidor do
48
+ * SDK v2 valida a própria saída e, se ela não obedece, devolve o erro em
49
+ * `isError` ("Output validation error"), não em exceção. Para afirmar uma
50
+ * recusa de propósito (parâmetro inválido), use `client.callTool` direto.
51
+ */
52
+ export async function chamarComoCliente(client, nome, args = {}) {
53
+ const r = await percorrer(client, nome, args);
54
+ if (r.isError) {
55
+ const texto = Array.isArray(r.content) ? r.content.map(c => ("text" in c ? c.text : "")).join(" ") : "";
56
+ throw new Error(`${nome} respondeu isError: ${texto}`);
57
+ }
58
+ return r;
59
+ }
60
+ /** Um valor do tipo errado para o tipo JSON Schema declarado. */
61
+ function tipoErrado(tipo) {
62
+ if (tipo === "string")
63
+ return 0;
64
+ if (typeof tipo === "string")
65
+ return "valor-de-tipo-errado";
66
+ return undefined;
67
+ }
68
+ /**
69
+ * As quebras genéricas, DERIVADAS do schema listado (não de nomes escritos à
70
+ * mão): `structuredContent` ausente, cada campo obrigatório ausente, o primeiro
71
+ * obrigatório com tipo declarado trocado de tipo.
72
+ */
73
+ export function quebrasDoSchema(outputSchema) {
74
+ const obrigatorios = Array.isArray(outputSchema.required) ? outputSchema.required : [];
75
+ const props = (outputSchema.properties ?? {});
76
+ const quebras = [
77
+ { descricao: "structuredContent ausente", adulterar: r => void delete r.structuredContent },
78
+ ...obrigatorios.map(campo => ({
79
+ descricao: `campo obrigatório ausente (${campo})`,
80
+ adulterar: (r) => void delete r.structuredContent?.[campo],
81
+ })),
82
+ ];
83
+ const tipado = obrigatorios.find(c => tipoErrado(props[c]?.type) !== undefined);
84
+ if (tipado) {
85
+ quebras.push({
86
+ descricao: `campo de tipo errado (${tipado})`,
87
+ adulterar: r => {
88
+ if (r.structuredContent)
89
+ r.structuredContent[tipado] = tipoErrado(props[tipado]?.type);
90
+ },
91
+ });
92
+ }
93
+ return quebras;
94
+ }
95
+ /**
96
+ * Controle negativo pelo lado do resultado: o servidor responde certo, o que
97
+ * chega ao cliente está quebrado, e a chamada TEM de falhar. Um servidor novo
98
+ * por quebra (`fabrica`), porque cada conexão leva a sua adulteração.
99
+ *
100
+ * Inclui a armadilha como veredito esperado "passa": sem `tools/list` antes, a
101
+ * primeira quebra passa calada. Se um dia o SDK passar a validar sem a lista,
102
+ * esse veredito acusa — e a regra do `listTools` pode ser revista.
103
+ *
104
+ * O teste afirma `obtido === esperado` em cada veredito; a `descricao` diz qual.
105
+ */
106
+ export async function controlesNegativos(fabrica, nome, args = {}, extras = []) {
107
+ const sonda = await conectarComoCliente(await fabrica());
108
+ let schema;
109
+ try {
110
+ const { tools } = await sonda.listTools();
111
+ schema = tools.find(t => t.name === nome)?.outputSchema;
112
+ }
113
+ finally {
114
+ await sonda.close();
115
+ }
116
+ if (!schema)
117
+ throw new Error(`${nome}: sem outputSchema no tools/list — não há contrato a provar`);
118
+ const quebras = [...quebrasDoSchema(schema), ...extras];
119
+ const rodar = async (q, listar) => {
120
+ const client = await conectarComoCliente(await fabrica(), { adulterar: q.adulterar });
121
+ try {
122
+ const r = listar ? await percorrer(client, nome, args) : await client.callTool({ name: nome, arguments: args });
123
+ // Erro de tool NÃO é reprovação do validador: a chamada-base tem de dar
124
+ // certo, ou o controle não prova nada — veredito que nunca confere.
125
+ if (r.isError)
126
+ return { obtido: "isError", mensagem: JSON.stringify(r.content) };
127
+ return { obtido: "passa" };
128
+ }
129
+ catch (e) {
130
+ return { obtido: "reprova", mensagem: e instanceof Error ? e.message : String(e) };
131
+ }
132
+ finally {
133
+ await client.close();
134
+ }
135
+ };
136
+ const vereditos = [];
137
+ for (const q of quebras)
138
+ vereditos.push({ descricao: q.descricao, esperado: "reprova", ...(await rodar(q, true)) });
139
+ const armadilha = quebras[1] ?? quebras[0];
140
+ vereditos.push({
141
+ descricao: `a armadilha: sem tools/list antes, "${armadilha.descricao}" passa calada`,
142
+ esperado: "passa",
143
+ ...(await rodar(armadilha, false)),
144
+ });
145
+ return vereditos;
146
+ }
147
+ //# sourceMappingURL=cliente.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cliente.js","sourceRoot":"","sources":["../src/cliente.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sbissoli/mcp-surface",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Impressão digital da superfície de um servidor MCP: initialize (instructions + capabilities) + tools/resources/prompts + quem responde sem token, travados ao lado da versão — mudou sem subir a versão = build vermelho e deploy recusado; mais a conferência do endpoint no ar e o replay das versões publicadas",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,6 +17,10 @@
17
17
  ".": {
18
18
  "types": "./dist/index.d.ts",
19
19
  "default": "./dist/index.js"
20
+ },
21
+ "./cliente": {
22
+ "types": "./dist/cliente.d.ts",
23
+ "default": "./dist/cliente.js"
20
24
  }
21
25
  },
22
26
  "bin": {
@@ -33,9 +37,16 @@
33
37
  "test": "vitest run"
34
38
  },
35
39
  "peerDependencies": {
40
+ "@modelcontextprotocol/client": "^2.0.0",
36
41
  "@modelcontextprotocol/server": "^2.0.0"
37
42
  },
43
+ "peerDependenciesMeta": {
44
+ "@modelcontextprotocol/client": {
45
+ "optional": true
46
+ }
47
+ },
38
48
  "devDependencies": {
49
+ "@modelcontextprotocol/client": "^2.1.0",
39
50
  "@modelcontextprotocol/server": "^2.1.0"
40
51
  },
41
52
  "keywords": [