#
tokens: 7231/50000 11/12 files (page 1/2)
lines: off (toggle) GitHub
raw markdown copy
This is page 1 of 2. Use http://codebase.md/tolik-unicornrider/mcp_scraper?page={x} to view the full context.

# Directory Structure

```
├── .gitignore
├── jest.config.js
├── mcp_docs
│   ├── mcp-llms-full.txt
│   └── mcp-typescript-sdk.txt
├── package-lock.json
├── package.json
├── README.md
├── src
│   ├── cli.ts
│   ├── data_processing.test.ts
│   ├── data_processing.ts
│   └── index.ts
├── test.html
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
node_modules
build

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# Website Scraper

A command-line tool and MCP server for scraping websites and converting HTML to Markdown.

## Features

- Extracts meaningful content from web pages using Mozilla's [Readability](https://github.com/mozilla/readability) library (the same engine used in Firefox's Reader View)
- Converts clean HTML to high-quality Markdown with TurndownService
- Securely handles HTML by removing potentially harmful script tags
- Works as both a command-line tool and an MCP server
- Supports direct conversion of local HTML files to Markdown

## Installation

```bash
# Install dependencies
npm install

# Build the project
npm run build

# Optionally, install globally
npm install -g .
```

## Usage

### CLI Mode

```bash
# Print output to console
scrape https://example.com

# Save output to a file
scrape https://example.com output.md

# Convert a local HTML file to Markdown
scrape --html-file input.html

# Convert a local HTML file and save output to a file
scrape --html-file input.html output.md

# Show help
scrape --help

# Or run via npm script
npm run start:cli -- https://example.com
```

### MCP Server Mode

This tool can be used as a Model Context Protocol (MCP) server:

```bash
# Start in MCP server mode
npm start
```

## Code Structure

- `src/index.ts` - Core functionality and MCP server implementation
- `src/cli.ts` - Command-line interface implementation
- `src/data_processing.ts` - HTML to Markdown conversion functionality

## API

The tool exports the following functions:

```typescript
// Scrape a website and convert to Markdown
import { scrapeToMarkdown } from './build/index.js';

// Convert HTML string to Markdown directly
import { htmlToMarkdown } from './build/data_processing.js';

async function example() {
  // Web scraping
  const markdown = await scrapeToMarkdown('https://example.com');
  console.log(markdown);
  
  // Direct HTML conversion
  const html = '<h1>Hello World</h1><p>This is <strong>bold</strong> text.</p>';
  const md = htmlToMarkdown(html);
  console.log(md);
}
```

## License

ISC 
```

--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------

```javascript
export default {
  preset: 'ts-jest',
  testEnvironment: 'node',
  extensionsToTreatAsEsm: ['.ts'],
  moduleNameMapper: {
    '^(\\.{1,2}/.*)\\.js$': '$1',
  },
  transform: {
    '^.+\\.tsx?$': [
      'ts-jest',
      {
        useESM: true,
      },
    ],
  },
}; 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

```

--------------------------------------------------------------------------------
/src/data_processing.ts:
--------------------------------------------------------------------------------

```typescript
import TurndownService from "turndown";

/**
 * Converts raw HTML to Markdown using TurndownService
 * Removes script tags for security and cleaner output
 * @param html Raw HTML string to convert
 * @returns Markdown string
 */
export function htmlToMarkdown(html: string): string {
  // Remove script tags and their content before conversion
  const cleanHtml = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
  
  const turndownService = new TurndownService({
    codeBlockStyle: 'fenced',
    emDelimiter: '_'
  });

  return turndownService.turndown(cleanHtml);
}



```

--------------------------------------------------------------------------------
/test.html:
--------------------------------------------------------------------------------

```html
<!DOCTYPE html>
<html>
<head>
  <title>Test HTML Document</title>
  <script>
    // This script should be removed in markdown
    console.log("This should not appear in the output");
  </script>
  <style>
    body { font-family: Arial, sans-serif; }
  </style>
</head>
<body>
  <h1>Test HTML Document</h1>
  <p>This is a <strong>test</strong> document with some <em>emphasized</em> text.</p>
  
  <h2>Features List</h2>
  <ul>
    <li>Headings</li>
    <li>Paragraphs</li>
    <li>Formatting (<strong>bold</strong>, <em>italic</em>)</li>
    <li>Lists</li>
    <li><a href="https://example.com">Links</a></li>
    <li>Code blocks</li>
  </ul>
  
  <h2>Code Example</h2>
  <pre><code>function test() {
  console.log("Hello, world!");
  return true;
}</code></pre>

  <script>
    // This inline script should also be removed
    document.getElementById("test").innerHTML = "Modified content";
  </script>
</body>
</html> 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "website-scraper",
  "version": "1.0.0",
  "description": "Command-line tool and MCP server for scraping websites and converting HTML to Markdown",
  "main": "build/index.js",
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "module",
  "bin": {
    "scrape": "./build/cli.js"
  },
  "scripts": {
    "build": "tsc && node -e \"require('fs').chmodSync('build/cli.js', '755')\"",
    "start": "npm run build && node build/index.js",
    "start:cli": "npm run build && node build/cli.js",
    "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
    "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch"
  },
  "files": [
    "build"
  ],
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.6.1",
    "@mozilla/readability": "^0.6.0",
    "jsdom": "^26.0.0",
    "node-html-markdown": "^1.3.0",
    "turndown": "^7.2.0",
    "zod": "^3.24.2"
  },
  "devDependencies": {
    "@types/jest": "^29.5.14",
    "@types/jsdom": "^21.1.7",
    "@types/node": "^22.13.9",
    "@types/turndown": "^5.0.5",
    "jest": "^29.7.0",
    "ts-jest": "^29.2.6",
    "typescript": "^5.8.2"
  }
}

```

--------------------------------------------------------------------------------
/src/cli.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node

import { scrapeToMarkdown } from './index.js';
import { htmlToMarkdown } from './data_processing.js';
import fs from 'fs/promises';
import path from 'path';

/**
 * CLI handler for website scraping and HTML conversion
 */
async function runCli() {
  const args = process.argv.slice(2);
  
  // Basic help
  if (args.includes('--help') || args.includes('-h') || args.length === 0) {
    console.log(`
Usage: scrape <url> [output_file]
   or: scrape --html-file <html_file> [output_file]
  
Options:
  --help, -h     Show this help message
  --html-file    Convert a local HTML file to Markdown instead of scraping a URL

Arguments:
  url/html_file  URL to scrape or path to HTML file (required)
  output_file    Output file path (optional, if not provided output is printed to stdout)
    `);
    process.exit(0);
  }

  // Check if we're converting a local HTML file
  const htmlFileMode = args.includes('--html-file');
  let targetInput: string;
  let outputFile: string | undefined;
  
  if (htmlFileMode) {
    const fileArgIndex = args.indexOf('--html-file') + 1;
    if (fileArgIndex >= args.length) {
      console.error('Error: No HTML file specified after --html-file');
      process.exit(1);
    }
    targetInput = args[fileArgIndex];
    outputFile = args[fileArgIndex + 1];
  } else {
    targetInput = args[0];
    outputFile = args[1];
  }
  
  try {
    let markdown: string;
    
    if (htmlFileMode) {
      // Direct HTML file to Markdown conversion
      try {
        const htmlContent = await fs.readFile(targetInput, 'utf-8');
        markdown = htmlToMarkdown(htmlContent);
      } catch (err: any) {
        throw new Error(`Error reading HTML file: ${err.message}`);
      }
    } else {
      // Web scraping mode
      markdown = await scrapeToMarkdown(targetInput);
    }
    
    if (outputFile) {
      await fs.writeFile(outputFile, markdown);
      console.error(`Markdown saved to: ${outputFile}`);
    } else {
      console.log(markdown);
    }
  } catch (error: any) {
    console.error(error.message);
    process.exit(1);
  }
}

// Run the CLI
runCli().catch((error) => {
  console.error("Fatal error:", error);
  process.exit(1);
}); 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { Readability } from '@mozilla/readability';
import { JSDOM } from 'jsdom';
import { htmlToMarkdown } from './data_processing.js';

/**
 * Scrapes a website and converts the HTML content to markdown
 * @param url The URL to scrape
 * @returns The markdown content as a string
 */
export async function scrapeToMarkdown(url: string): Promise<string> {
  try {
    // Fetch the HTML content from the provided URL with proper headers
    const response = await fetch(url, {
      headers: {
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Accept-Language': 'en-US,en;q=0.5',
      }
    });
    
    if (!response.ok) {
      throw new Error(`Failed to fetch URL: ${response.status}`);
    }
    
    // Get content type to check encoding
    const contentType = response.headers.get('content-type') || '';
    const htmlContent = await response.text();

    // Parse the HTML using JSDOM with the URL to resolve relative links
    const dom = new JSDOM(htmlContent, { 
      url,
      pretendToBeVisual: true, // This helps with some interactive content
    });
    
    // Extract the main content using Readability
    const reader = new Readability(dom.window.document);
    const article = reader.parse();
    
    if (!article || !article.content) {
      throw new Error("Failed to parse article content");
    }
    
    // Convert the cleaned article HTML to Markdown using htmlToMarkdown
    let markdown = htmlToMarkdown(article.content);
    
    // Simple post-processing to improve code blocks with language hints
    markdown = markdown.replace(/```\n(class|function|import|const|let|var|if|for|while)/g, '```javascript\n$1');
    markdown = markdown.replace(/```\n(def|class|import|from|with|if|for|while)(\s+)/g, '```python\n$1$2');
    
    return markdown;
  } catch (error: any) {
    throw new Error(`Scraping error: ${error.message}`);
  }
}




// Create an MCP server instance
const server = new McpServer({
  name: "ScrapeServer",
  version: "1.0.0"
});

// Register the "scrape-to-markdown" tool
server.tool(
  "scrape-to-markdown",
  { url: z.string().url() },
  async ({ url }) => {
    try {
      const markdown = await scrapeToMarkdown(url);
      
      // Return the markdown as the tool result
      return {
        content: [{ type: "text", text: markdown }]
      };
    } catch (error: any) {
      // Handle errors gracefully
      return {
        content: [{ type: "text", text: `Error: ${error.message}` }],
        isError: true
      };
    }
  }
);

// Start the MCP server using stdio transport
// const transport = new StdioServerTransport();
// await server.connect(transport);

async function main() {
  // Create a stdio transport for communication
  // This allows the server to communicate with clients via standard input/output
  const transport = new StdioServerTransport();

  // Connect the server to the transport
  // This starts listening for incoming messages and enables communication
  await server.connect(transport);

  // Log a message to indicate the server is running
  // Note: Using console.error instead of console.log because stdout is used for MCP communication
  console.error("Weather MCP Server running on stdio");
}

// Call the main function and handle any fatal errors
main().catch((error) => {
  console.error("Fatal error in main():", error);
  process.exit(1); // Exit with error code 1 if there's a fatal error
});

```

--------------------------------------------------------------------------------
/src/data_processing.test.ts:
--------------------------------------------------------------------------------

```typescript
import { htmlToMarkdown } from './data_processing.js';

describe('htmlToMarkdown', () => {
  test('converts basic HTML to markdown', () => {
    const html = '<p>Hello world</p>';
    expect(htmlToMarkdown(html)).toBe('Hello world');
  });

  test('converts headings', () => {
    const html = '<h1>Title</h1><h2>Subtitle</h2>';
    expect(htmlToMarkdown(html)).toBe('Title\n=====\n\nSubtitle\n--------');
  });

  test('converts emphasis using underscores', () => {
    const html = '<em>emphasized text</em>';
    expect(htmlToMarkdown(html)).toBe('_emphasized text_');
  });

  test('converts strong', () => {
    const html = '<strong>bold text</strong>';
    expect(htmlToMarkdown(html)).toBe('**bold text**');
  });

  test('converts code blocks with fencing', () => {
    const html = '<pre><code>const x = 1;</code></pre>';
    expect(htmlToMarkdown(html)).toBe('```\nconst x = 1;\n```');
  });

  test('converts links', () => {
    const html = '<a href="https://example.com">Example</a>';
    expect(htmlToMarkdown(html)).toBe('[Example](https://example.com)');
  });

  test('converts lists', () => {
    const html = '<ul><li>Item 1</li><li>Item 2</li></ul>';
    expect(htmlToMarkdown(html)).toBe('*   Item 1\n*   Item 2');
  });

  test('converts nested HTML structures', () => {
    const html = '<div><h1>Title</h1><p>Paragraph with <strong>bold</strong> and <em>emphasis</em>.</p></div>';
    expect(htmlToMarkdown(html)).toBe('Title\n=====\n\nParagraph with **bold** and _emphasis_.');
  });

  test('handles empty input', () => {
    expect(htmlToMarkdown('')).toBe('');
  });

  test('handles input with no HTML tags', () => {
    const text = 'Plain text without any HTML';
    expect(htmlToMarkdown(text)).toBe('Plain text without any HTML');
  });

  test('handles complex HTML page with script tags', () => {
    const html = `
      <!DOCTYPE html>
      <html>
        <head>
          <title>Test Page</title>
          <script>
            console.log("This script should be removed");
            function test() {
              return "Hello World";
            }
          </script>
          <style>
            body { font-family: Arial, sans-serif; }
          </style>
        </head>
        <body>
          <header>
            <h1>Page Header</h1>
            <nav>
              <ul>
                <li><a href="#section1">Section 1</a></li>
                <li><a href="#section2">Section 2</a></li>
              </ul>
            </nav>
          </header>
          <main>
            <section id="section1">
              <h2>Section 1 Title</h2>
              <p>This is <em>emphasized</em> and <strong>strong</strong> text.</p>
              <script>document.write("This should not appear in markdown");</script>
              <pre><code>const codeExample = "This should be formatted as code";</code></pre>
            </section>
            <section id="section2">
              <h2>Section 2 Title</h2>
              <p>Another paragraph with a <a href="https://example.com">link</a>.</p>
              <table>
                <tr>
                  <th>Header 1</th>
                  <th>Header 2</th>
                </tr>
                <tr>
                  <td>Cell 1</td>
                  <td>Cell 2</td>
                </tr>
              </table>
            </section>
          </main>
          <footer>
            <p>Page Footer</p>
            <script>
              // This script should also be removed
              const footer = document.querySelector('footer');
            </script>
          </footer>
        </body>
      </html>
    `;
    
    const expectedMarkdown = `Test Page body { font-family: Arial, sans-serif; }

Page Header
===========

*   [Section 1](#section1)
*   [Section 2](#section2)

Section 1 Title
---------------

This is _emphasized_ and **strong** text.

\`\`\`
const codeExample = "This should be formatted as code";
\`\`\`

Section 2 Title
---------------

Another paragraph with a [link](https://example.com).

Header 1

Header 2

Cell 1

Cell 2

Page Footer`.trim();

    const output = htmlToMarkdown(html).trim();
    expect(output).toBe(expectedMarkdown);
    
  });
}); 
```

--------------------------------------------------------------------------------
/mcp_docs/mcp-typescript-sdk.txt:
--------------------------------------------------------------------------------

```
# MCP TypeScript SDK ![NPM Version](https://img.shields.io/npm/v/%40modelcontextprotocol%2Fsdk) ![MIT licensed](https://img.shields.io/npm/l/%40modelcontextprotocol%2Fsdk)

## Table of Contents
- [Overview](#overview)
- [Installation](#installation)
- [Quickstart](#quickstart)
- [What is MCP?](#what-is-mcp)
- [Core Concepts](#core-concepts)
  - [Server](#server)
  - [Resources](#resources)
  - [Tools](#tools)
  - [Prompts](#prompts)
- [Running Your Server](#running-your-server)
  - [stdio](#stdio)
  - [HTTP with SSE](#http-with-sse)
  - [Testing and Debugging](#testing-and-debugging)
- [Examples](#examples)
  - [Echo Server](#echo-server)
  - [SQLite Explorer](#sqlite-explorer)
- [Advanced Usage](#advanced-usage)
  - [Low-Level Server](#low-level-server)
  - [Writing MCP Clients](#writing-mcp-clients)
  - [Server Capabilities](#server-capabilities)

## Overview

The Model Context Protocol allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction. This TypeScript SDK implements the full MCP specification, making it easy to:

- Build MCP clients that can connect to any MCP server
- Create MCP servers that expose resources, prompts and tools
- Use standard transports like stdio and SSE
- Handle all MCP protocol messages and lifecycle events

## Installation

```bash
npm install @modelcontextprotocol/sdk
```

## Quick Start

Let's create a simple MCP server that exposes a calculator tool and some data:

```typescript
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Create an MCP server
const server = new McpServer({
  name: "Demo",
  version: "1.0.0"
});

// Add an addition tool
server.tool("add",
  { a: z.number(), b: z.number() },
  async ({ a, b }) => ({
    content: [{ type: "text", text: String(a + b) }]
  })
);

// Add a dynamic greeting resource
server.resource(
  "greeting",
  new ResourceTemplate("greeting://{name}", { list: undefined }),
  async (uri, { name }) => ({
    contents: [{
      uri: uri.href,
      text: `Hello, ${name}!`
    }]
  })
);

// Start receiving messages on stdin and sending messages on stdout
const transport = new StdioServerTransport();
await server.connect(transport);
```

## What is MCP?

The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. Think of it like a web API, but specifically designed for LLM interactions. MCP servers can:

- Expose data through **Resources** (think of these sort of like GET endpoints; they are used to load information into the LLM's context)
- Provide functionality through **Tools** (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect)
- Define interaction patterns through **Prompts** (reusable templates for LLM interactions)
- And more!

## Core Concepts

### Server

The McpServer is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing:

```typescript
const server = new McpServer({
  name: "My App",
  version: "1.0.0"
});
```

### Resources

Resources are how you expose data to LLMs. They're similar to GET endpoints in a REST API - they provide data but shouldn't perform significant computation or have side effects:

```typescript
// Static resource
server.resource(
  "config",
  "config://app",
  async (uri) => ({
    contents: [{
      uri: uri.href,
      text: "App configuration here"
    }]
  })
);

// Dynamic resource with parameters
server.resource(
  "user-profile",
  new ResourceTemplate("users://{userId}/profile", { list: undefined }),
  async (uri, { userId }) => ({
    contents: [{
      uri: uri.href,
      text: `Profile data for user ${userId}`
    }]
  })
);
```

### Tools

Tools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects:

```typescript
// Simple tool with parameters
server.tool(
  "calculate-bmi",
  {
    weightKg: z.number(),
    heightM: z.number()
  },
  async ({ weightKg, heightM }) => ({
    content: [{
      type: "text",
      text: String(weightKg / (heightM * heightM))
    }]
  })
);

// Async tool with external API call
server.tool(
  "fetch-weather",
  { city: z.string() },
  async ({ city }) => {
    const response = await fetch(`https://api.weather.com/${city}`);
    const data = await response.text();
    return {
      content: [{ type: "text", text: data }]
    };
  }
);
```

### Prompts

Prompts are reusable templates that help LLMs interact with your server effectively:

```typescript
server.prompt(
  "review-code",
  { code: z.string() },
  ({ code }) => ({
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: `Please review this code:\n\n${code}`
      }
    }]
  })
);
```

## Running Your Server

MCP servers in TypeScript need to be connected to a transport to communicate with clients. How you start the server depends on the choice of transport:

### stdio

For command-line tools and direct integrations:

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new McpServer({
  name: "example-server",
  version: "1.0.0"
});

// ... set up server resources, tools, and prompts ...

const transport = new StdioServerTransport();
await server.connect(transport);
```

### HTTP with SSE

For remote servers, start a web server with a Server-Sent Events (SSE) endpoint, and a separate endpoint for the client to send its messages to:

```typescript
import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";

const server = new McpServer({
  name: "example-server",
  version: "1.0.0"
});

// ... set up server resources, tools, and prompts ...

const app = express();

app.get("/sse", async (req, res) => {
  const transport = new SSEServerTransport("/messages", res);
  await server.connect(transport);
});

app.post("/messages", async (req, res) => {
  // Note: to support multiple simultaneous connections, these messages will
  // need to be routed to a specific matching transport. (This logic isn't
  // implemented here, for simplicity.)
  await transport.handlePostMessage(req, res);
});

app.listen(3001);
```

### Testing and Debugging

To test your server, you can use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector). See its README for more information.

## Examples

### Echo Server

A simple server demonstrating resources, tools, and prompts:

```typescript
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

const server = new McpServer({
  name: "Echo",
  version: "1.0.0"
});

server.resource(
  "echo",
  new ResourceTemplate("echo://{message}", { list: undefined }),
  async (uri, { message }) => ({
    contents: [{
      uri: uri.href,
      text: `Resource echo: ${message}`
    }]
  })
);

server.tool(
  "echo",
  { message: z.string() },
  async ({ message }) => ({
    content: [{ type: "text", text: `Tool echo: ${message}` }]
  })
);

server.prompt(
  "echo",
  { message: z.string() },
  ({ message }) => ({
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: `Please process this message: ${message}`
      }
    }]
  })
);
```

### SQLite Explorer

A more complex example showing database integration:

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import sqlite3 from "sqlite3";
import { promisify } from "util";
import { z } from "zod";

const server = new McpServer({
  name: "SQLite Explorer",
  version: "1.0.0"
});

// Helper to create DB connection
const getDb = () => {
  const db = new sqlite3.Database("database.db");
  return {
    all: promisify<string, any[]>(db.all.bind(db)),
    close: promisify(db.close.bind(db))
  };
};

server.resource(
  "schema",
  "schema://main",
  async (uri) => {
    const db = getDb();
    try {
      const tables = await db.all(
        "SELECT sql FROM sqlite_master WHERE type='table'"
      );
      return {
        contents: [{
          uri: uri.href,
          text: tables.map((t: {sql: string}) => t.sql).join("\n")
        }]
      };
    } finally {
      await db.close();
    }
  }
);

server.tool(
  "query",
  { sql: z.string() },
  async ({ sql }) => {
    const db = getDb();
    try {
      const results = await db.all(sql);
      return {
        content: [{
          type: "text",
          text: JSON.stringify(results, null, 2)
        }]
      };
    } catch (err: unknown) {
      const error = err as Error;
      return {
        content: [{
          type: "text",
          text: `Error: ${error.message}`
        }],
        isError: true
      };
    } finally {
      await db.close();
    }
  }
);
```

## Advanced Usage

### Low-Level Server

For more control, you can use the low-level Server class directly:

```typescript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  ListPromptsRequestSchema,
  GetPromptRequestSchema
} from "@modelcontextprotocol/sdk/types.js";

const server = new Server(
  {
    name: "example-server",
    version: "1.0.0"
  },
  {
    capabilities: {
      prompts: {}
    }
  }
);

server.setRequestHandler(ListPromptsRequestSchema, async () => {
  return {
    prompts: [{
      name: "example-prompt",
      description: "An example prompt template",
      arguments: [{
        name: "arg1",
        description: "Example argument",
        required: true
      }]
    }]
  };
});

server.setRequestHandler(GetPromptRequestSchema, async (request) => {
  if (request.params.name !== "example-prompt") {
    throw new Error("Unknown prompt");
  }
  return {
    description: "Example prompt",
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: "Example prompt text"
      }
    }]
  };
});

const transport = new StdioServerTransport();
await server.connect(transport);
```

### Writing MCP Clients

The SDK provides a high-level client interface:

```typescript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

const transport = new StdioClientTransport({
  command: "node",
  args: ["server.js"]
});

const client = new Client(
  {
    name: "example-client",
    version: "1.0.0"
  },
  {
    capabilities: {
      prompts: {},
      resources: {},
      tools: {}
    }
  }
);

await client.connect(transport);

// List prompts
const prompts = await client.listPrompts();

// Get a prompt
const prompt = await client.getPrompt("example-prompt", {
  arg1: "value"
});

// List resources
const resources = await client.listResources();

// Read a resource
const resource = await client.readResource("file:///example.txt");

// Call a tool
const result = await client.callTool({
  name: "example-tool",
  arguments: {
    arg1: "value"
  }
});
```

## Documentation

- [Model Context Protocol documentation](https://modelcontextprotocol.io)
- [MCP Specification](https://spec.modelcontextprotocol.io)
- [Example Servers](https://github.com/modelcontextprotocol/servers)

## Contributing

Issues and pull requests are welcome on GitHub at https://github.com/modelcontextprotocol/typescript-sdk.

## License

This project is licensed under the MIT License—see the [LICENSE](LICENSE) file for details.

```
Page 1/2FirstPrevNextLast