npm package: @strawberryprotocol/str-toolserver
Description: Strawberry.fun Toolserver framework
Author: Strawberry Dot Fun Team
Latest version: 0.0.1

# Strawberry.fun Toolserver

This library contains the code for writing a tool server for Strawberry.fun.

Agents discover tools and pay to use them. This library is designed for tool authors to quickly create and deploy a new tool, which they can monetize through a token and fees.

## Installation

```bash
npm install @strawberryprotocol/str-toolserver
```

## Basic Usage

```typescript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { strawberry } from "@strawberryprotocol/str-toolserver";

function createServer() {
  const server = new Server(
    { name: "my-server", version: "1.0.0" },
    { capabilities: { tools: {} } }
  );

  // Add your tool handlers here
  // ...

  return server;
}

const app = strawberry(createServer);
app.connect();
app.listen(3000);
```

## Authentication & Authorization

The str-toolserver package provides built-in support for EIP-712 signature verification and token gating:

### Signature Verification

Use `createSignatureVerifier` to verify that requests are signed by a specific Ethereum address:

```typescript
import { createSignatureVerifier } from "@strawberryprotocol/str-toolserver";

const verifier = createSignatureVerifier({
  domain: {
    name: "Strawberry.fun",
    version: "1",
    chainId: 84532, // Base Sepolia
  },
});

server.setRequestHandler(CallToolRequestSchema, async request => {
  // This will throw if signature is invalid
  const signerAddress = await verifier(request);
  
  return {
    content: [{ 
      type: "text", 
      text: `Message from ${signerAddress}: ${request.params.arguments?.message}` 
    }],
  };
});
```

### Token Gating

Use `createTokenGateVerifier` to require users to hold a minimum balance of a specific token:

```typescript
import { createTokenGateVerifier } from "@strawberryprotocol/str-toolserver";
import { createPublicClient, http } from "viem";
import { baseSepolia } from "viem/chains";

// Create a viem client for reading token balances
const client = createPublicClient({
  chain: baseSepolia,
  transport: http(),
});

const verifier = createTokenGateVerifier(client, {
  domain: {
    name: "Strawberry.fun",
    version: "1",
    chainId: 84532, // Base Sepolia
  },
  tokenAddress: "0x1234..." as Address, // Your token contract address
  minTokenBalance: 1n, // Minimum required balance
});

server.setRequestHandler(CallToolRequestSchema, async request => {
  try {
    // This will throw if signature is invalid or token balance is insufficient
    const { signerAddress, balance } = await verifier(request);
    
    return {
      content: [{ 
        type: "text", 
        text: `Authorized request from ${signerAddress} (balance: ${balance})` 
      }],
    };
  } catch (error) {
    throw new Error(`Access denied: ${error.message}`);
  }
});
```

### Tool Schema Requirements

When using signature verification, your tool's input schema must include a `strawberry_signature` parameter:

```typescript
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [{
    name: "my-tool",
    description: "A token-gated tool",
    inputSchema: {
      type: "object",
      properties: {
        message: { 
          type: "string", 
          description: "Your message" 
        },
        strawberry_signature: { 
          type: "string", 
          description: "EIP-712 signature of the request parameters" 
        },
      },
      required: ["message", "strawberry_signature"],
    },
  }],
}));
```

### Error Handling

The auth functions throw specific error types that you can handle:

- `SignatureVerificationError`: Thrown when signature verification fails
- `TokenBalanceError`: Thrown when token balance is insufficient

## Examples

Check out the `examples` directory for complete working examples:

- `hello-world-typescript`: Basic server setup
- `hello-world-typescript/index-gated.ts`: Token-gated server example

## Environment Variables

When using token gating, you'll need to set:

- `LOCALHOST_TOKEN`: The address of your ERC20 token contract

## Contributing

See [CONTRIBUTING.md](../CONTRIBUTING.md) for more information.