Skip to content

Commit 4a9afb5

Browse files
Merge pull request #16 from sveltejs/restructure
2 parents e68067e + 0c35883 commit 4a9afb5

File tree

13 files changed

+614
-116
lines changed

13 files changed

+614
-116
lines changed

.cocoignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.claude
2+
.github

.vscode/mcp-snippets.code-snippets

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
// Place your svelte-mcp workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
3+
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
4+
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
5+
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
6+
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
7+
// Placeholders with the same ids are connected.
8+
// Example:
9+
"Setup Function": {
10+
"scope": "javascript,typescript",
11+
"prefix": "!setup-mcp",
12+
"body": [
13+
"import type { SvelteMcp } from '../../index.js';",
14+
"import * as v from 'valibot';",
15+
"",
16+
"export function ${1:function_name}(server: SvelteMcp) {",
17+
"\t$0",
18+
"}",
19+
],
20+
"description": "Create a setup function for a tool/resource/prompt handler",
21+
},
22+
"Autofixer": {
23+
"scope": "javascript,typescript",
24+
"prefix": "!autofixer",
25+
"body": [
26+
"import type { Autofixer } from '.';",
27+
"export const ${1:autofixer_name}: Autofixer = {",
28+
"\t$0",
29+
"};",
30+
],
31+
"description": "Create a setup export for an autofixer",
32+
},
33+
}

docs/tmcp.md

Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
> [!WARNING]
2+
> Unfortunately i published the 1.0 by mistake...this package is currently under heavy development so there will be breaking changes in minors...threat this `1.x` as the `0.x` of any other package. Sorry for the disservice, every breaking will be properly labeled in the PR name.
3+
4+
# tmcp
5+
6+
A lightweight, schema-agnostic Model Context Protocol (MCP) server implementation with unified API design.
7+
8+
## Why tmcp?
9+
10+
tmcp offers significant advantages over the official MCP SDK:
11+
12+
- **🔄 Schema Agnostic**: Works with any validation library through adapters
13+
- **📦 No Weird Dependencies**: Minimal footprint with only essential dependencies (looking at you `express`)
14+
- **🎯 Unified API**: Consistent, intuitive interface across all MCP capabilities
15+
- **🔌 Extensible**: Easy to add support for new schema libraries
16+
- **⚡ Lightweight**: No bloat, just what you need
17+
18+
## Supported Schema Libraries
19+
20+
tmcp works with all major schema validation libraries through its adapter system:
21+
22+
- **Zod** - `@tmcp/adapter-zod`
23+
- **Valibot** - `@tmcp/adapter-valibot`
24+
- **ArkType** - `@tmcp/adapter-arktype`
25+
- **Effect Schema** - `@tmcp/adapter-effect`
26+
- **Zod v3** - `@tmcp/adapter-zod-v3`
27+
28+
## Installation
29+
30+
```bash
31+
pnpm install tmcp
32+
# Choose your preferred schema library adapter
33+
pnpm install @tmcp/adapter-zod zod
34+
# Choose your preferred transport
35+
pnpm install @tmcp/transport-stdio # For CLI/desktop apps
36+
pnpm install @tmcp/transport-http # For web-based clients
37+
```
38+
39+
## Quick Start
40+
41+
### Standard I/O Transport (CLI/Desktop)
42+
43+
```javascript
44+
import { McpServer } from 'tmcp';
45+
import { ZodJsonSchemaAdapter } from '@tmcp/adapter-zod';
46+
import { StdioTransport } from '@tmcp/transport-stdio';
47+
import { z } from 'zod';
48+
49+
const adapter = new ZodJsonSchemaAdapter();
50+
const server = new McpServer(
51+
{
52+
name: 'my-server',
53+
version: '1.0.0',
54+
description: 'My awesome MCP server',
55+
},
56+
{
57+
adapter,
58+
capabilities: {
59+
tools: { listChanged: true },
60+
prompts: { listChanged: true },
61+
resources: { listChanged: true },
62+
},
63+
},
64+
);
65+
66+
// Define a tool with type-safe schema
67+
server.tool(
68+
{
69+
name: 'calculate',
70+
description: 'Perform mathematical calculations',
71+
schema: z.object({
72+
operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
73+
a: z.number(),
74+
b: z.number(),
75+
}),
76+
},
77+
async ({ operation, a, b }) => {
78+
switch (operation) {
79+
case 'add':
80+
return a + b;
81+
case 'subtract':
82+
return a - b;
83+
case 'multiply':
84+
return a * b;
85+
case 'divide':
86+
return a / b;
87+
}
88+
},
89+
);
90+
91+
// Start the server with stdio transport
92+
const transport = new StdioTransport(server);
93+
transport.listen();
94+
```
95+
96+
### HTTP Transport (Web-based)
97+
98+
```javascript
99+
import { McpServer } from 'tmcp';
100+
import { ZodJsonSchemaAdapter } from '@tmcp/adapter-zod';
101+
import { HttpTransport } from '@tmcp/transport-http';
102+
import { z } from 'zod';
103+
104+
const adapter = new ZodJsonSchemaAdapter();
105+
const server = new McpServer(/* ... same server config ... */);
106+
107+
// Add tools as above...
108+
109+
// Create HTTP transport
110+
const transport = new HttpTransport(server);
111+
112+
// Use with your preferred HTTP server (Bun example)
113+
Bun.serve({
114+
port: 3000,
115+
async fetch(req) {
116+
const response = await transport.respond(req);
117+
if (response === null) {
118+
return new Response('Not Found', { status: 404 });
119+
}
120+
return response;
121+
},
122+
});
123+
```
124+
125+
## API Reference
126+
127+
### McpServer
128+
129+
The main server class that handles MCP protocol communications.
130+
131+
#### Constructor
132+
133+
```javascript
134+
new McpServer(serverInfo, options);
135+
```
136+
137+
- `serverInfo`: Server metadata (name, version, description)
138+
- `options`: Configuration object with adapter and capabilities
139+
140+
#### Methods
141+
142+
##### `tool(definition, handler)`
143+
144+
Register a tool with optional schema validation.
145+
146+
```javascript
147+
server.tool(
148+
{
149+
name: 'tool-name',
150+
description: 'Tool description',
151+
schema: yourSchema, // optional
152+
},
153+
async (input) => {
154+
// Tool implementation
155+
return result;
156+
},
157+
);
158+
```
159+
160+
##### `prompt(definition, handler)`
161+
162+
Register a prompt template with optional schema validation.
163+
164+
```javascript
165+
server.prompt(
166+
{
167+
name: 'prompt-name',
168+
description: 'Prompt description',
169+
schema: yourSchema, // optional
170+
complete: (arg, context) => ['completion1', 'completion2'] // optional
171+
},
172+
async (input) => {
173+
// Prompt implementation
174+
return { messages: [...] };
175+
}
176+
);
177+
```
178+
179+
##### `resource(definition, handler)`
180+
181+
Register a static resource.
182+
183+
```javascript
184+
server.resource(
185+
{
186+
name: 'resource-name',
187+
description: 'Resource description',
188+
uri: 'file://path/to/resource'
189+
},
190+
async (uri, params) => {
191+
// Resource implementation
192+
return { contents: [...] };
193+
}
194+
);
195+
```
196+
197+
##### `template(definition, handler)`
198+
199+
Register a URI template for dynamic resources.
200+
201+
```javascript
202+
server.template(
203+
{
204+
name: 'template-name',
205+
description: 'Template description',
206+
uri: 'file://path/{id}/resource',
207+
complete: (arg, context) => ['id1', 'id2'] // optional
208+
},
209+
async (uri, params) => {
210+
// Template implementation using params.id
211+
return { contents: [...] };
212+
}
213+
);
214+
```
215+
216+
##### `receive(request)`
217+
218+
Process an incoming MCP request.
219+
220+
```javascript
221+
const response = server.receive(jsonRpcRequest);
222+
```
223+
224+
## Advanced Examples
225+
226+
### Multiple Schema Libraries
227+
228+
```javascript
229+
// Use different schemas for different tools
230+
import { z } from 'zod';
231+
import * as v from 'valibot';
232+
233+
server.tool(
234+
{
235+
name: 'zod-tool',
236+
schema: z.object({ name: z.string() }),
237+
},
238+
async ({ name }) => `Hello ${name}`,
239+
);
240+
241+
server.tool(
242+
{
243+
name: 'valibot-tool',
244+
schema: v.object({ age: v.number() }),
245+
},
246+
async ({ age }) => `Age: ${age}`,
247+
);
248+
```
249+
250+
### Resource Templates with Completion
251+
252+
```javascript
253+
server.template(
254+
{
255+
name: 'user-profile',
256+
description: 'Get user profile by ID',
257+
uri: 'users/{userId}/profile',
258+
complete: (arg, context) => {
259+
// Provide completions for userId parameter
260+
return ['user1', 'user2', 'user3'];
261+
},
262+
},
263+
async (uri, params) => {
264+
const user = await getUserById(params.userId);
265+
return {
266+
contents: [
267+
{
268+
uri,
269+
mimeType: 'application/json',
270+
text: JSON.stringify(user),
271+
},
272+
],
273+
};
274+
},
275+
);
276+
```
277+
278+
### Complex Validation
279+
280+
```javascript
281+
const complexSchema = z.object({
282+
user: z.object({
283+
name: z.string().min(1),
284+
email: z.string().email(),
285+
age: z.number().min(18).max(120),
286+
}),
287+
preferences: z
288+
.object({
289+
theme: z.enum(['light', 'dark']),
290+
notifications: z.boolean(),
291+
})
292+
.optional(),
293+
tags: z.array(z.string()).default([]),
294+
});
295+
296+
server.tool(
297+
{
298+
name: 'create-user',
299+
description: 'Create a new user with preferences',
300+
schema: complexSchema,
301+
},
302+
async (input) => {
303+
// Input is fully typed and validated
304+
const { user, preferences, tags } = input;
305+
return await createUser(user, preferences, tags);
306+
},
307+
);
308+
```
309+
310+
## Contributing
311+
312+
Contributions are welcome! Please see our [contributing guidelines](../../CONTRIBUTING.md) for details.
313+
314+
## Acknowledgments
315+
316+
Huge thanks to Sean O'Bannon that provided us with the `@tmcp` scope on npm.
317+
318+
## License
319+
320+
MIT © Paolo Ricciuti

src/lib/mcp/handlers/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { SvelteMcp } from '../index.js';
2+
import * as prompts from './prompts/index.js';
3+
import * as tools from './tools/index.js';
4+
import * as resources from './resources/index.js';
5+
6+
export function setup_tools(server: SvelteMcp) {
7+
for (const tool in tools) {
8+
tools[tool as keyof typeof tools](server);
9+
}
10+
}
11+
12+
export function setup_prompts(server: SvelteMcp) {
13+
for (const prompt in prompts) {
14+
prompts[prompt as keyof typeof prompts](server);
15+
}
16+
}
17+
18+
export function setup_resources(server: SvelteMcp) {
19+
for (const resource in resources) {
20+
resources[resource as keyof typeof resources](server);
21+
}
22+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './svelte-task.js';

0 commit comments

Comments
 (0)