Skip to content
2 changes: 2 additions & 0 deletions .cocoignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.claude
.github
33 changes: 33 additions & 0 deletions .vscode/mcp-snippets.code-snippets
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
// Place your svelte-mcp workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
// Placeholders with the same ids are connected.
// Example:
"Setup Function": {
"scope": "javascript,typescript",
"prefix": "!setup-mcp",
"body": [
"import type { SvelteMcp } from '../../index.js';",
"import * as v from 'valibot';",
"",
"export function ${1:function_name}(server: SvelteMcp) {",
"\t$0",
"}",
],
"description": "Create a setup function for a tool/resource/prompt handler",
},
"Autofixer": {
"scope": "javascript,typescript",
"prefix": "!autofixer",
"body": [
"import type { Autofixer } from '.';",
"export const ${1:autofixer_name}: Autofixer = {",
"\t$0",
"};",
],
"description": "Create a setup export for an autofixer",
},
}
320 changes: 320 additions & 0 deletions docs/tmcp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
> [!WARNING]
> 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.

# tmcp

A lightweight, schema-agnostic Model Context Protocol (MCP) server implementation with unified API design.

## Why tmcp?

tmcp offers significant advantages over the official MCP SDK:

- **🔄 Schema Agnostic**: Works with any validation library through adapters
- **📦 No Weird Dependencies**: Minimal footprint with only essential dependencies (looking at you `express`)
- **🎯 Unified API**: Consistent, intuitive interface across all MCP capabilities
- **🔌 Extensible**: Easy to add support for new schema libraries
- **⚡ Lightweight**: No bloat, just what you need

## Supported Schema Libraries

tmcp works with all major schema validation libraries through its adapter system:

- **Zod** - `@tmcp/adapter-zod`
- **Valibot** - `@tmcp/adapter-valibot`
- **ArkType** - `@tmcp/adapter-arktype`
- **Effect Schema** - `@tmcp/adapter-effect`
- **Zod v3** - `@tmcp/adapter-zod-v3`

## Installation

```bash
pnpm install tmcp
# Choose your preferred schema library adapter
pnpm install @tmcp/adapter-zod zod
# Choose your preferred transport
pnpm install @tmcp/transport-stdio # For CLI/desktop apps
pnpm install @tmcp/transport-http # For web-based clients
```

## Quick Start

### Standard I/O Transport (CLI/Desktop)

```javascript
import { McpServer } from 'tmcp';
import { ZodJsonSchemaAdapter } from '@tmcp/adapter-zod';
import { StdioTransport } from '@tmcp/transport-stdio';
import { z } from 'zod';

const adapter = new ZodJsonSchemaAdapter();
const server = new McpServer(
{
name: 'my-server',
version: '1.0.0',
description: 'My awesome MCP server',
},
{
adapter,
capabilities: {
tools: { listChanged: true },
prompts: { listChanged: true },
resources: { listChanged: true },
},
},
);

// Define a tool with type-safe schema
server.tool(
{
name: 'calculate',
description: 'Perform mathematical calculations',
schema: z.object({
operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
a: z.number(),
b: z.number(),
}),
},
async ({ operation, a, b }) => {
switch (operation) {
case 'add':
return a + b;
case 'subtract':
return a - b;
case 'multiply':
return a * b;
case 'divide':
return a / b;
}
},
);

// Start the server with stdio transport
const transport = new StdioTransport(server);
transport.listen();
```

### HTTP Transport (Web-based)

```javascript
import { McpServer } from 'tmcp';
import { ZodJsonSchemaAdapter } from '@tmcp/adapter-zod';
import { HttpTransport } from '@tmcp/transport-http';
import { z } from 'zod';

const adapter = new ZodJsonSchemaAdapter();
const server = new McpServer(/* ... same server config ... */);

// Add tools as above...

// Create HTTP transport
const transport = new HttpTransport(server);

// Use with your preferred HTTP server (Bun example)
Bun.serve({
port: 3000,
async fetch(req) {
const response = await transport.respond(req);
if (response === null) {
return new Response('Not Found', { status: 404 });
}
return response;
},
});
```

## API Reference

### McpServer

The main server class that handles MCP protocol communications.

#### Constructor

```javascript
new McpServer(serverInfo, options);
```

- `serverInfo`: Server metadata (name, version, description)
- `options`: Configuration object with adapter and capabilities

#### Methods

##### `tool(definition, handler)`

Register a tool with optional schema validation.

```javascript
server.tool(
{
name: 'tool-name',
description: 'Tool description',
schema: yourSchema, // optional
},
async (input) => {
// Tool implementation
return result;
},
);
```

##### `prompt(definition, handler)`

Register a prompt template with optional schema validation.

```javascript
server.prompt(
{
name: 'prompt-name',
description: 'Prompt description',
schema: yourSchema, // optional
complete: (arg, context) => ['completion1', 'completion2'] // optional
},
async (input) => {
// Prompt implementation
return { messages: [...] };
}
);
```

##### `resource(definition, handler)`

Register a static resource.

```javascript
server.resource(
{
name: 'resource-name',
description: 'Resource description',
uri: 'file://path/to/resource'
},
async (uri, params) => {
// Resource implementation
return { contents: [...] };
}
);
```

##### `template(definition, handler)`

Register a URI template for dynamic resources.

```javascript
server.template(
{
name: 'template-name',
description: 'Template description',
uri: 'file://path/{id}/resource',
complete: (arg, context) => ['id1', 'id2'] // optional
},
async (uri, params) => {
// Template implementation using params.id
return { contents: [...] };
}
);
```

##### `receive(request)`

Process an incoming MCP request.

```javascript
const response = server.receive(jsonRpcRequest);
```

## Advanced Examples

### Multiple Schema Libraries

```javascript
// Use different schemas for different tools
import { z } from 'zod';
import * as v from 'valibot';

server.tool(
{
name: 'zod-tool',
schema: z.object({ name: z.string() }),
},
async ({ name }) => `Hello ${name}`,
);

server.tool(
{
name: 'valibot-tool',
schema: v.object({ age: v.number() }),
},
async ({ age }) => `Age: ${age}`,
);
```

### Resource Templates with Completion

```javascript
server.template(
{
name: 'user-profile',
description: 'Get user profile by ID',
uri: 'users/{userId}/profile',
complete: (arg, context) => {
// Provide completions for userId parameter
return ['user1', 'user2', 'user3'];
},
},
async (uri, params) => {
const user = await getUserById(params.userId);
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(user),
},
],
};
},
);
```

### Complex Validation

```javascript
const complexSchema = z.object({
user: z.object({
name: z.string().min(1),
email: z.string().email(),
age: z.number().min(18).max(120),
}),
preferences: z
.object({
theme: z.enum(['light', 'dark']),
notifications: z.boolean(),
})
.optional(),
tags: z.array(z.string()).default([]),
});

server.tool(
{
name: 'create-user',
description: 'Create a new user with preferences',
schema: complexSchema,
},
async (input) => {
// Input is fully typed and validated
const { user, preferences, tags } = input;
return await createUser(user, preferences, tags);
},
);
```

## Contributing

Contributions are welcome! Please see our [contributing guidelines](../../CONTRIBUTING.md) for details.

## Acknowledgments

Huge thanks to Sean O'Bannon that provided us with the `@tmcp` scope on npm.

## License

MIT © Paolo Ricciuti
22 changes: 22 additions & 0 deletions src/lib/mcp/handlers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { SvelteMcp } from '../index.js';
import * as prompts from './prompts/index.js';
import * as tools from './tools/index.js';
import * as resources from './resources/index.js';

export function setup_tools(server: SvelteMcp) {
for (const tool in tools) {
tools[tool as keyof typeof tools](server);
}
}

export function setup_prompts(server: SvelteMcp) {
for (const prompt in prompts) {
prompts[prompt as keyof typeof prompts](server);
}
}

export function setup_resources(server: SvelteMcp) {
for (const resource in resources) {
resources[resource as keyof typeof resources](server);
}
}
1 change: 1 addition & 0 deletions src/lib/mcp/handlers/prompts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './svelte-task.js';
Loading