@ottocode/sdk 0.1.200 → 0.1.202

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ export type MCPTransport = 'stdio' | 'http' | 'sse';
2
+ export type MCPScope = 'global' | 'project';
3
+
4
+ export interface MCPOAuthConfig {
5
+ clientId?: string;
6
+ callbackPort?: number;
7
+ scopes?: string[];
8
+ }
9
+
10
+ export interface MCPServerConfig {
11
+ name: string;
12
+ transport?: MCPTransport;
13
+
14
+ command?: string;
15
+ args?: string[];
16
+ env?: Record<string, string>;
17
+ cwd?: string;
18
+
19
+ url?: string;
20
+ headers?: Record<string, string>;
21
+
22
+ oauth?: MCPOAuthConfig;
23
+
24
+ disabled?: boolean;
25
+
26
+ scope?: MCPScope;
27
+ }
28
+
29
+ export interface MCPConfig {
30
+ servers: MCPServerConfig[];
31
+ }
32
+
33
+ export interface MCPServerStatus {
34
+ name: string;
35
+ connected: boolean;
36
+ tools: string[];
37
+ error?: string;
38
+ transport?: MCPTransport;
39
+ url?: string;
40
+ authRequired?: boolean;
41
+ authenticated?: boolean;
42
+ }
@@ -24,14 +24,14 @@ export function buildRipgrepTool(projectRoot: string): {
24
24
  .array(z.string())
25
25
  .optional()
26
26
  .describe('One or more glob patterns to include'),
27
- maxResults: z.number().int().min(1).max(5000).optional().default(500),
27
+ maxResults: z.number().int().min(1).max(5000).optional().default(100),
28
28
  }),
29
29
  async execute({
30
30
  query,
31
31
  path = '.',
32
32
  ignoreCase,
33
33
  glob,
34
- maxResults = 500,
34
+ maxResults = 100,
35
35
  }: {
36
36
  query: string;
37
37
  path?: string;
@@ -95,12 +95,20 @@ export function buildRipgrepTool(projectRoot: string): {
95
95
  .split('\n')
96
96
  .filter(Boolean)
97
97
  .slice(0, maxResults);
98
+ const TEXT_MAX = 200;
98
99
  const matches = lines.map((l) => {
99
100
  const m = l.match(/^(.+?):(\d+):(.*)$/s);
100
- if (!m) return { file: '', line: 0, text: l };
101
+ if (!m)
102
+ return {
103
+ file: '',
104
+ line: 0,
105
+ text: l.length > TEXT_MAX ? l.slice(0, TEXT_MAX) + '…' : l,
106
+ };
101
107
  const file = m[1];
102
108
  const line = Number.parseInt(m[2], 10);
103
- const text = m[3];
109
+ const raw = m[3];
110
+ const text =
111
+ raw.length > TEXT_MAX ? raw.slice(0, TEXT_MAX) + '…' : raw;
104
112
  return { file, line, text };
105
113
  });
106
114
  resolve({ ok: true, count: matches.length, matches });
@@ -6,7 +6,6 @@ import { buildGitTools } from './builtin/git.ts';
6
6
  import { progressUpdateTool } from './builtin/progress.ts';
7
7
  import { buildBashTool } from './builtin/bash.ts';
8
8
  import { buildRipgrepTool } from './builtin/ripgrep.ts';
9
- import { buildGrepTool } from './builtin/grep.ts';
10
9
  import { buildGlobTool } from './builtin/glob.ts';
11
10
  import { buildApplyPatchTool } from './builtin/patch.ts';
12
11
  import { buildEditTool } from './builtin/edit.ts';
@@ -16,6 +15,8 @@ import { buildWebSearchTool } from './builtin/websearch.ts';
16
15
  import { buildTerminalTool } from './builtin/terminal.ts';
17
16
  import type { TerminalManager } from '../terminals/index.ts';
18
17
  import { initializeSkills, buildSkillTool } from '../../../skills/index.ts';
18
+ import { getMCPManager } from '../mcp/index.ts';
19
+ import { convertMCPToolsToAISDK } from '../mcp/tools.ts';
19
20
  import fg from 'fast-glob';
20
21
  import { dirname, isAbsolute, join } from 'node:path';
21
22
  import { pathToFileURL } from 'node:url';
@@ -120,8 +121,6 @@ export async function discoverProjectTools(
120
121
  // Search
121
122
  const rg = buildRipgrepTool(projectRoot);
122
123
  tools.set(rg.name, rg.tool);
123
- const grep = buildGrepTool(projectRoot);
124
- tools.set(grep.name, grep.tool);
125
124
  const glob = buildGlobTool(projectRoot);
126
125
  tools.set(glob.name, glob.tool);
127
126
  // Patch/apply
@@ -148,6 +147,14 @@ export async function discoverProjectTools(
148
147
  const skillTool = buildSkillTool();
149
148
  tools.set(skillTool.name, skillTool.tool);
150
149
 
150
+ const mcpManager = getMCPManager();
151
+ if (mcpManager?.started) {
152
+ const mcpTools = convertMCPToolsToAISDK(mcpManager);
153
+ for (const { name, tool } of mcpTools) {
154
+ tools.set(name, tool);
155
+ }
156
+ }
157
+
151
158
  async function loadFromBase(base: string | null | undefined) {
152
159
  if (!base) return;
153
160
  try {
package/src/index.ts CHANGED
@@ -171,6 +171,8 @@ export {
171
171
  getGlobalToolsDir,
172
172
  getGlobalCommandsDir,
173
173
  getSecureAuthPath,
174
+ getSecureBaseDir,
175
+ getSecureOAuthDir,
174
176
  getHomeDir,
175
177
  } from './config/src/paths.ts';
176
178
  export {
@@ -333,3 +335,33 @@ export {
333
335
  } from './tunnel/index.ts';
334
336
 
335
337
  export type { TunnelConnection, TunnelEvents } from './tunnel/index.ts';
338
+
339
+ // =======================
340
+ // MCP (Model Context Protocol)
341
+ // =======================
342
+ export {
343
+ MCPClientWrapper,
344
+ MCPServerManager,
345
+ convertMCPToolsToAISDK,
346
+ getMCPManager,
347
+ initializeMCP,
348
+ shutdownMCP,
349
+ loadMCPConfig,
350
+ addMCPServerToConfig,
351
+ removeMCPServerFromConfig,
352
+ OAuthCredentialStore,
353
+ OttoOAuthProvider,
354
+ OAuthCallbackServer,
355
+ } from './core/src/index.ts';
356
+ export type {
357
+ MCPServerConfig,
358
+ MCPConfig,
359
+ MCPServerStatus,
360
+ MCPToolInfo,
361
+ MCPTransport,
362
+ MCPOAuthConfig,
363
+ MCPScope,
364
+ StoredOAuthData,
365
+ OttoOAuthProviderOptions,
366
+ CallbackResult,
367
+ } from './core/src/index.ts';
@@ -1,134 +0,0 @@
1
- import { tool, type Tool } from 'ai';
2
- import { z } from 'zod/v3';
3
- import { spawn } from 'node:child_process';
4
- import { join } from 'node:path';
5
- import DESCRIPTION from './grep.txt' with { type: 'text' };
6
- import { defaultIgnoreGlobs } from './ignore.ts';
7
- import { createToolError, type ToolResponse } from '../error.ts';
8
- import { resolveBinary } from '../bin-manager.ts';
9
-
10
- function expandTilde(p: string) {
11
- const home = process.env.HOME || process.env.USERPROFILE || '';
12
- if (!home) return p;
13
- if (p === '~') return home;
14
- if (p.startsWith('~/')) return `${home}/${p.slice(2)}`;
15
- return p;
16
- }
17
-
18
- export function buildGrepTool(projectRoot: string): {
19
- name: string;
20
- tool: Tool;
21
- } {
22
- const grep = tool({
23
- description: DESCRIPTION,
24
- inputSchema: z.object({
25
- pattern: z
26
- .string()
27
- .describe('Regex pattern to search for in file contents'),
28
- path: z
29
- .string()
30
- .optional()
31
- .describe('Directory to search in (default: project root).'),
32
- include: z
33
- .string()
34
- .optional()
35
- .describe('File glob to include (e.g., "*.js", "*.{ts,tsx}")'),
36
- ignore: z
37
- .array(z.string())
38
- .optional()
39
- .describe('Glob patterns to exclude from search'),
40
- }),
41
- async execute(params: {
42
- pattern: string;
43
- path?: string;
44
- include?: string;
45
- ignore?: string[];
46
- }): Promise<
47
- ToolResponse<{
48
- count: number;
49
- matches: Array<{ file: string; line: number; text: string }>;
50
- }>
51
- > {
52
- const pattern = String(params.pattern || '');
53
- if (!pattern) {
54
- return createToolError('pattern is required', 'validation', {
55
- parameter: 'pattern',
56
- suggestion: 'Provide a regex pattern to search for',
57
- });
58
- }
59
-
60
- const p = expandTilde(String(params.path || '')).trim();
61
- const isAbs = p.startsWith('/') || /^[A-Za-z]:[\\/]/.test(p);
62
- const searchPath = p ? (isAbs ? p : join(projectRoot, p)) : projectRoot;
63
-
64
- const rgBin = await resolveBinary('rg');
65
- const args: string[] = ['-n', '--color', 'never'];
66
- for (const g of defaultIgnoreGlobs(params.ignore)) {
67
- args.push('--glob', g);
68
- }
69
- if (params.include) {
70
- args.push('--glob', params.include);
71
- }
72
- args.push(pattern, searchPath);
73
-
74
- let output = '';
75
- try {
76
- output = await new Promise<string>((resolve, reject) => {
77
- const proc = spawn(rgBin, args, { cwd: projectRoot });
78
- let stdout = '';
79
- let stderr = '';
80
- proc.stdout.on('data', (d) => {
81
- stdout += d.toString();
82
- });
83
- proc.stderr.on('data', (d) => {
84
- stderr += d.toString();
85
- });
86
- proc.on('close', (code) => {
87
- if (code === 1) resolve('');
88
- else if (code !== 0)
89
- reject(new Error(stderr.trim() || 'ripgrep failed'));
90
- else resolve(stdout);
91
- });
92
- proc.on('error', reject);
93
- });
94
- } catch (error: unknown) {
95
- const err2 = error as { message?: string };
96
- return createToolError(`ripgrep failed: ${err2.message}`, 'execution', {
97
- parameter: 'pattern',
98
- value: pattern,
99
- suggestion:
100
- 'Check if ripgrep (rg) is installed and the pattern is valid',
101
- });
102
- }
103
-
104
- const lines = output.trim().split('\n');
105
- const matches: Array<{
106
- file: string;
107
- line: number;
108
- text: string;
109
- }> = [];
110
-
111
- for (const line of lines) {
112
- if (!line) continue;
113
- const m = line.match(/^(.+?):(\d+):(.*)$/s);
114
- if (!m) continue;
115
- const filePath = m[1];
116
- const lineNum = parseInt(m[2], 10);
117
- const lineText = m[3];
118
- if (!filePath || !Number.isFinite(lineNum)) continue;
119
- matches.push({ file: filePath, line: lineNum, text: lineText });
120
- }
121
-
122
- const limit = 500;
123
- const truncated = matches.length > limit;
124
- const finalMatches = truncated ? matches.slice(0, limit) : matches;
125
-
126
- return {
127
- ok: true,
128
- count: finalMatches.length,
129
- matches: finalMatches,
130
- };
131
- },
132
- });
133
- return { name: 'grep', tool: grep };
134
- }
@@ -1,9 +0,0 @@
1
- - Fast content search tool powered by ripgrep (rg)
2
- - Supports full regex syntax (e.g., "log.*Error", "function\\s+\\w+")
3
- - Optional include glob to filter files (e.g., "*.js", "*.{ts,tsx}")
4
- - Returns files with at least one match and line previews, sorted by modification time
5
- - Skips common build and cache folders by default; add 'ignore' patterns to refine
6
-
7
- Usage tips:
8
- - For counting matches, use the Bash tool with rg directly (do not use grep)
9
- - Batch multiple searches when exploring a codebase broadly