Skip to content

Commit b685e23

Browse files
authored
Merge pull request #12 from code-rabi/benr/custom-endpoints-docs
chore: update docs
2 parents 74f03b5 + b4c353f commit b685e23

3 files changed

Lines changed: 182 additions & 16 deletions

File tree

‎AGENTS.md‎

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,31 @@
3131

3232
- Meta-tools return JSON with `success` and `message` fields. Read `message` to adapt decisions (e.g., policy denial, already active, limits exceeded).
3333

34-
## HTTP (debugging only)
34+
## HTTP endpoints
3535

36-
- Endpoints: `GET /healthz`, `GET /tools`, `POST/GET/DELETE /mcp`, `GET /.well-known/mcp-config`.
37-
- Headers: use `mcp-client-id` to reuse per-client server bundles; `mcp-session-id` is managed by the MCP transport after initialize.
36+
### Built-in MCP endpoints
37+
38+
- `GET /healthz` - Health check
39+
- `GET /tools` - List available toolsets and tools
40+
- `POST /mcp` - MCP JSON-RPC requests
41+
- `GET /mcp` - Server-sent events stream
42+
- `DELETE /mcp` - Close session
43+
- `GET /.well-known/mcp-config` - Configuration schema
44+
45+
### Custom HTTP endpoints
46+
47+
Servers may expose custom REST-like endpoints alongside MCP protocol endpoints. These are defined by the server implementer and provide direct HTTP access to functionality.
48+
49+
- Custom endpoints use standard HTTP methods (GET, POST, PUT, DELETE, PATCH)
50+
- Request/response validation via Zod schemas
51+
- Access to client ID via `mcp-client-id` header
52+
- Permission-aware endpoints receive client's allowed toolsets
53+
- Standard error format with `VALIDATION_ERROR`, `INTERNAL_ERROR`, or `RESPONSE_VALIDATION_ERROR` codes
54+
55+
Check `GET /tools` or server documentation to discover available custom endpoints.
56+
57+
### Headers
58+
59+
- `mcp-client-id`: Client identifier (reuse for per-client sessions)
60+
- `mcp-session-id`: Session identifier (managed by MCP transport after initialize)
61+
- `mcp-toolset-permissions`: Comma-separated toolset list (permission-based servers with header-based permissions)

‎README.md‎

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- [Static startup](#static-startup)
1111
- [Permission-based starter guide](#permission-based-starter-guide)
1212
- [Permission configuration approaches](#permission-configuration-approaches)
13+
- [Custom HTTP endpoints](#custom-http-endpoints)
1314
- [API](#api)
1415
- [createMcpServer](#createmcpserveroptions)
1516
- [createPermissionBasedMcpServer](#createpermissionbasedmcpserveroptions)
@@ -589,6 +590,135 @@ await start();
589590

590591
**Note:** Resolver functions must be synchronous. If you need to fetch permissions from external sources, do so before server creation and cache the results.
591592

593+
## Custom HTTP endpoints
594+
595+
Toolception supports custom HTTP endpoints alongside MCP protocol endpoints, enabling REST-like APIs with Zod validation and type inference.
596+
597+
### Basic usage
598+
599+
```ts
600+
import { createMcpServer, defineEndpoint } from "toolception";
601+
import { z } from "zod";
602+
603+
const { start } = await createMcpServer({
604+
// ... standard options
605+
http: {
606+
port: 3000,
607+
customEndpoints: [
608+
defineEndpoint({
609+
method: "GET",
610+
path: "/api/users",
611+
querySchema: z.object({
612+
limit: z.coerce.number().int().positive().default(10),
613+
role: z.enum(["admin", "user"]).optional(),
614+
}),
615+
responseSchema: z.object({
616+
users: z.array(z.object({ id: z.string(), name: z.string() })),
617+
}),
618+
handler: async (req) => {
619+
// req.query is typed: { limit: number, role?: "admin" | "user" }
620+
// req.clientId is available from mcp-client-id header
621+
return { users: [{ id: "1", name: "Alice" }] };
622+
},
623+
}),
624+
],
625+
},
626+
});
627+
```
628+
629+
### Validation schemas
630+
631+
- **querySchema**: Validates URL query parameters (use `z.coerce` for type conversion)
632+
- **bodySchema**: Validates request body (POST/PUT/PATCH)
633+
- **paramsSchema**: Validates path parameters (e.g., `/users/:userId`)
634+
- **responseSchema**: Validates handler response (prevents invalid data leakage)
635+
636+
### Request context
637+
638+
Handlers receive a typed request object:
639+
640+
```ts
641+
{
642+
body: TBody, // Validated from bodySchema
643+
query: TQuery, // Validated from querySchema
644+
params: TParams, // Validated from paramsSchema
645+
headers: Record<string, string | string[] | undefined>,
646+
clientId: string, // From mcp-client-id header or auto-generated
647+
}
648+
```
649+
650+
### Permission-aware endpoints
651+
652+
Use `definePermissionAwareEndpoint` in permission-based servers to access client permissions:
653+
654+
```ts
655+
import { createPermissionBasedMcpServer, definePermissionAwareEndpoint } from "toolception";
656+
657+
const { start } = await createPermissionBasedMcpServer({
658+
// ... permission config
659+
http: {
660+
customEndpoints: [
661+
definePermissionAwareEndpoint({
662+
method: "GET",
663+
path: "/api/me",
664+
responseSchema: z.object({
665+
clientId: z.string(),
666+
allowedToolsets: z.array(z.string()),
667+
isAdmin: z.boolean(),
668+
}),
669+
handler: async (req) => {
670+
// req.allowedToolsets and req.failedToolsets are available
671+
return {
672+
clientId: req.clientId,
673+
allowedToolsets: req.allowedToolsets,
674+
isAdmin: req.allowedToolsets.includes("admin-tools"),
675+
};
676+
},
677+
}),
678+
],
679+
},
680+
});
681+
```
682+
683+
### Error handling
684+
685+
Validation failures return standardized error responses:
686+
687+
- **400 VALIDATION_ERROR**: Request validation failed (body, query, or params)
688+
- **500 INTERNAL_ERROR**: Handler threw an exception
689+
- **500 RESPONSE_VALIDATION_ERROR**: Response validation failed
690+
691+
Example error response:
692+
693+
```json
694+
{
695+
"error": {
696+
"code": "VALIDATION_ERROR",
697+
"message": "Validation failed for query",
698+
"details": [
699+
{
700+
"code": "invalid_type",
701+
"path": ["limit"],
702+
"message": "Expected number, received string"
703+
}
704+
]
705+
}
706+
}
707+
```
708+
709+
### Reserved paths
710+
711+
Custom endpoints cannot override built-in MCP paths:
712+
713+
- `/mcp` - MCP JSON-RPC endpoint
714+
- `/healthz` - Health check
715+
- `/tools` - Tool listing
716+
- `/.well-known/mcp-config` - Configuration schema
717+
718+
### Complete example
719+
720+
See `examples/custom-endpoints-demo.ts` for a full working example with GET, POST, PUT, DELETE endpoints, pagination, and permission-aware handlers.
721+
592722
## API
593723

594724
### createMcpServer(options)
@@ -736,9 +866,10 @@ const moduleLoaders = {
736866

737867
#### options.http (optional)
738868

739-
`{ host?: string; port?: number; basePath?: string; cors?: boolean; logger?: boolean }`
869+
`{ host?: string; port?: number; basePath?: string; cors?: boolean; logger?: boolean; customEndpoints?: CustomEndpointDefinition[] }`
740870

741871
- Fastify transport configuration. Defaults: host `0.0.0.0`, port `3000`, basePath `/`, CORS enabled, logger disabled.
872+
- `customEndpoints`: Optional array of custom HTTP endpoints to register alongside MCP protocol endpoints. See [Custom HTTP endpoints](#custom-http-endpoints) for details.
742873

743874
#### options.createServer (optional)
744875

‎examples/custom-endpoints-demo.ts‎

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,32 @@
11
#!/usr/bin/env tsx
22

33
/**
4-
* Example server demonstrating custom endpoint functionality.
4+
* Custom Endpoints Demo - Comprehensive examples of HTTP endpoint integration
55
*
6-
* This example shows:
7-
* - Basic GET/POST/PUT/DELETE endpoints with Zod validation
8-
* - Query parameter validation and coercion
9-
* - Path parameter validation
10-
* - Request body validation
11-
* - Response validation
12-
* - Client ID extraction
13-
* - Permission-aware endpoints
6+
* This example demonstrates:
147
*
15-
* Run this example with:
16-
* npx tsx examples/custom-endpoints-demo.ts
8+
* VALIDATION:
9+
* - Query parameter validation with Zod (coercion, defaults, enums)
10+
* - Path parameter validation (/users/:userId)
11+
* - Request body validation (POST/PUT)
12+
* - Response schema validation
1713
*
18-
* Then test the endpoints with curl:
14+
* FEATURES:
15+
* - All HTTP methods: GET, POST, PUT, DELETE
16+
* - Client ID extraction from mcp-client-id header
17+
* - Permission-aware endpoints (allowedToolsets, failedToolsets)
18+
* - Type-safe handlers with automatic inference
19+
* - Standard error responses (VALIDATION_ERROR, INTERNAL_ERROR)
20+
*
21+
* EXAMPLES:
22+
* 1. Standard server with custom endpoints (port 3000)
23+
* 2. Permission-based server with permission-aware endpoints (port 3001)
24+
*
25+
* Run this example:
26+
* npx tsx examples/custom-endpoints-demo.ts # Standard server
27+
* npx tsx examples/custom-endpoints-demo.ts permission # Permission-based server
28+
*
29+
* Quick test:
1930
* curl "http://localhost:3000/api/users?limit=5"
2031
* curl -X POST http://localhost:3000/api/users -H 'Content-Type: application/json' -d '{"name":"Alice","email":"alice@example.com"}'
2132
*/

0 commit comments

Comments
 (0)